# -*- coding: utf-8 -*-

from odoo import api, fields, models, tools, _, SUPERUSER_ID
from odoo.exceptions import UserError, ValidationError, RedirectWarning
from odoo.addons.workflow.tools.date import lang_datetime
import dateutil
import time
import datetime
import copy
from cStringIO import StringIO
import traceback
from datetime import timedelta
from pytz import timezone
from odoo.tools.safe_eval import safe_eval, test_python_expr
from odoo.addons.queue_job.job import job
import json
import re
from odoo.addons.workflow.tools.format import format_amount
from odoo.tools.misc import profile as graph_profile
from odoo.addons.workflow.tools.profiler import profile as log_profile
from odoo.addons.queue_job.job import job
import decimal
import inspect
from odoo.addons.workflow.models.license import prpcrypt
from odoo.addons.kthrp_dingding.interface import dingding_interface

URGENCY_MODEL = ['kthrp.stock.requisition.order','kthrp.purchase.requisition','kthrp.purchase.order','kthrp.contract.contract',
                 'kthrp.payable.payment.request','kthrp.expense.report', 'kthrp.expense.expense.requisition', 'kthrp.expense.borrowing', 'kthrp.asset.maintenance.repair.requisition']

EXIST_WECHAT_MODEL = ['kthrp.purchase.order', 'kthrp.purchase.requisition', 'kthrp.contract.contract',
                   'kthrp.payable.payment.request','kthrp.payable.invoice','kthrp.expense.expense.requisition',
                   'kthrp.expense.report', 'kthrp.purchase.supplier.requisition','kthrp.asset.acceptance',
                   'kthrp.asset.sundry.request', 'kthrp.jiahui.request.platform']


def _t(lang, values):
    if lang == 'zh_CN' and len(values) > 1:
        return values[1]
    else:
        return values[0]


def condition_boolean(something):
    if something:
        result = True
    else:
        result = False
    return result


DEFAULT_PYTHON_CODE = """# Available variables:
#  - time, datetime, dateutil, timezone: Python libraries
#  - env: Kthrp Environement
#  - model: Model of the approve document
#  - record: The approve document
#  - condition_boolean: transfer something to boolean exp: condition_1 = condition_boolean(record)
#  - candidate The candidate approver id of this workflow line
# To return an 'result', assign: result = condition_1 and condition_2\n\n\n\n"""


class WorkflowInterface(models.Model):
    _name = "workflow.interface"
    _description = "Kthrp Base Workflow Interface"
    _inherit = "mail.thread"
    _going_live_clear_cls = 'clearable'

    _state_selection = {
        'created': 'created',
        'approving': 'approving',
        'approved': 'approved',
        'rejected': 'rejected',
    }

    task_ids = fields.One2many(comodel_name="workflow.task", compute="_get_wkf_task", string='Approve Tasks')

    # 分配给当前用户审批的task
    approve_task_id = fields.Many2one(string="Approve Task", compute="_get_approve_task",
                                      comodel_name="workflow.task")
    # 是否有相应的审批按钮
    has_approve = fields.Boolean(compute='_compute_has_approve')
    has_reject = fields.Boolean(compute='_compute_has_reject')
    has_transfer = fields.Boolean(compute='_compute_has_transfer')
    has_return = fields.Boolean(compute='_compute_has_return')
    has_withdraw = fields.Boolean(compute='_compute_has_withdraw')

    has_linkup = fields.Boolean(compute='_compute_has_linkup')
    has_reply = fields.Boolean(compute='_compute_has_reply')

    has_add = fields.Boolean(compute='_compute_has_add')
    # 用来控制审批记录界面延迟显示
    delay_display = fields.Boolean(compute='_compute_delay_display', default=False)

    # 提交时间
    submit_time = fields.Datetime()
    submit_date = fields.Date(compute="_compute_submit_date", store=True)
    # 收回单据时间
    withdraw_time = fields.Datetime()

    # 单据提交用户
    submit_user_id = fields.Many2one(comodel_name="res.users")
    # 单据提交用户关联的员工
    submit_employee_id = fields.Many2one(comodel_name="hr.employee", string="Submit Employee",
                                         compute='_compute_submit_employee', store=True)
    workflow_id = fields.Many2one(comodel_name='workflow', compute='_compute_workflow_id')
    display_type = fields.Selection([('tree', 'Tree'), ('diagram', 'Diagram')],
                                    default='tree')
    # 神秘数字
    mysterious_figures = fields.Integer(default=0, compute='_compute_mysterious_figures')
    is_creator = fields.Boolean(compute='_compute_is_creator', default=True)
    display_cancel_button = fields.Boolean(compute='_compute_display_cancel_button', default=False)

    # 最后催办时间
    last_reminders_time = fields.Datetime()
    # 是否退回
    is_wkf_return = fields.Boolean()


    # 用于对每个审批节点对单据的校验
    @api.multi
    def action_approve_check(self):
        pass

    def pre_approve_confirm(self):
        pass

    def approve_confirm(self):
        if self.approve_document:
            return self.approve_document.pre_approve_confirm()

    #出入库平台群组
    def get_has_group(self):
        return False

    # 判断工作流模型取消按钮是否对创建者可见
    @api.multi
    def _compute_display_cancel_button(self):
        has_group = self.get_has_group()
        for rec in self:
            if not isinstance(rec.id, int):
                rec.display_cancel_button = False
            else:
                if SUPERUSER_ID == self.env.user.id:
                    rec.display_cancel_button = True
                elif rec.create_uid.id == self.env.user.id or has_group:
                    wkf_model_record = self.env['workflow.model'].sudo().search(
                        [('model_id.model', '=', rec._name)], limit=1)
                    if wkf_model_record:
                        if hasattr(rec, 'state'):
                            if rec.state == 'rejected':
                                rec.display_cancel_button = True
                            elif rec.state in ('draft', 'created') and wkf_model_record.created_show_cancel:
                                rec.display_cancel_button = True
                            elif rec.state == 'approved' and wkf_model_record.approved_show_cancel:
                                rec.display_cancel_button = True
                            elif rec._name == 'kthrp.payable.payment.request' and rec.state == 'audited':
                                rec.display_cancel_button = True
                            else:
                                rec.display_cancel_button = False
                        elif hasattr(rec, 'status'):
                            if rec.status == 'rejected':
                                rec.display_cancel_button = True
                            elif rec.status in ('draft', 'created') and wkf_model_record.created_show_cancel:
                                rec.display_cancel_button = True
                            elif rec.staus == 'approved' and wkf_model_record.approved_show_cancel:
                                rec.display_cancel_button = True
                            else:
                                rec.display_cancel_button = False
                        else:
                            rec.display_cancel_button = False
                    else:
                        rec.display_cancel_button = False
                else:
                    if rec._name == 'kthrp.sales.receivable.sales.invoice' and self.env.user.has_group('kthrp_payable.group_payable_invoice_cancel') and \
                            rec.state in ('created','approved','audited','rejected'):
                        rec.display_cancel_button = True
                    else:
                        rec.display_cancel_button = False

    # kthrp报表平台打印审批记录，安装pingan时优先取eoa审批记录
    @api.model
    def compute_approval_page(self):
        if self.has_install_module('kthrp_pingan_ext') and hasattr(self, 'eoa_task_ids') and self.eoa_task_ids:
            return self.eoa_task_ids
        else:
            return self.task_ids.filtered(lambda x: x.approve_result_id.code in ('complete', 'approve')) or []

    @api.multi
    def prepare_submit(self, submit_function_name):
        if len(self) > 1:
            return False
        if self.env.context.get('no_need_prepare') or not self.env.context.get('from_button'):
            return False
        self.with_context(from_button=False)
        self = self.suspend_security()
        prepare_obj = self.env['workflow.submit.prepare'].with_context(workflow_model=self._name,submit_function_name=submit_function_name,x_active_id=self.id)
        return prepare_obj.wizard_init()

    @api.multi
    def _compute_is_creator(self):
        has_group = self.get_has_group()
        user_id = self.env.user.id
        for rec in self:
            if rec.create_uid.id == user_id or SUPERUSER_ID == user_id or has_group:
                result = True
            else:
                result = False
            if result != rec.is_creator:
                rec.is_creator = result

    #######  移动端审批通用 方法  #######

    # 个单据可继承改写，从而走个性化 审批
    # 不继承，需配置   设置/配置/移动端配置/移动审批配置  对应模型

    # 用于各单据统一继承来获取工作流单据列表信息(不继)
    @api.multi
    def document_info_list(self):

        # 2019-12-15  移动端审批启用可配置
        # type: 针对单据页面模型的移动端审批根据单据类型来配置的情况
        self.ensure_one()
        self = self.with_context(lang='zh_CN')
        type = self.env.context.get('map_approve_type', False)
        approve_setting = self.get_mapp_approve_setting(type=type)
        record = self
        if not approve_setting.department_field_id and not approve_setting.department_py:
            show_dept_flag, department = False, ''
        else:
            show_dept_flag, department = True, self.get_field_vale(record, approve_setting.department_field_id.name,
                                                                   code=approve_setting.department_py or None)
        vals = {'document_type': approve_setting.card_name_py and self.get_field_vale(record, False,
                                                                                      code=approve_setting.card_name_py,
                                                                                      special=True) or approve_setting.card_name or '',
                'document_name': self.get_field_vale(record, approve_setting.doc_number_field_id.name,
                                                     code=approve_setting.doc_number_py or None),
                'note': self.get_field_vale(record, approve_setting.note_field_id.name,
                                            code=approve_setting.note_py or None),
                'company': self.get_field_vale(record, approve_setting.company_field_id.name,
                                               code=approve_setting.company_py or None),
                'show_dept_flag': show_dept_flag,
                'department': department,
                'is_general_map': True,  # 是常规模型的移动审批通用配置
                'approve_setting': approve_setting.id,  # 记录对应的 移动审批通用配置 的id
                # 'map_approve_type': type
                }
        return vals

    # 用于各单据统一继承来获取工作流单据表单信息
    @api.multi
    def document_info(self, task_id, open_record=None, approve_setting=None, get_url_doc=False):
        if not open_record:
            self.ensure_one()
        self = self.suspend_security()
        lang = self.env.user.lang
        type = self.env.context.get('map_approve_type', False)
        approve_setting = approve_setting or self.get_mapp_approve_setting(type=type)
        display_list = []
        display_style_dict = {}
        record = open_record or self
        # 获取头信息
        if approve_setting.head_info_ids:
            # 可折叠的组type为  'grouplist', 不可折叠的组： 'grouptext'
            group_num = 0
            content = []
            group_dict = {}
            for line in approve_setting.head_info_ids:
                # 当一个组元素都不存在时
                if not group_num:
                    if line.mobile_display_type in ('text', 'accordionText'):
                        display_list.append({'key': line.show_name or line.field_id.field_description,
                                             'value': self.get_field_vale(record, line.field_id.name,
                                                                          code=line.mobile_python_code),
                                             'type': line.mobile_display_type})
                    elif line.mobile_display_type == 'list':
                        display_list.append({'key': line.show_name or line.field_id.field_description,
                                             'value': self.get_field_vale(record, line.field_id.name,
                                                                           code=line.mobile_python_code),
                                             'type': line.mobile_display_type})
                    elif line.mobile_display_type == 'url':
                        can_url = False
                        # 如果字段为many2one 才提供 连接，否则按text
                        if not get_url_doc and line.field_id.ttype == 'many2one':
                            field_ref_doc = getattr(record, line.field_id.name)
                            if field_ref_doc:
                                field_ref_doc_type = self.env['workflow.task'].get_approve_document_type(approve_document=field_ref_doc)
                                field_ref_doc_setting = field_ref_doc.get_mapp_approve_setting(type=field_ref_doc_type)
                                # 仅当 字段的模型 有配置对应的 移动审批配置，才可点开
                                if field_ref_doc_setting:
                                    can_url = True
                                    display_list.append({'key': line.show_name or line.field_id.field_description,
                                                         'value': self.get_field_vale(record, line.field_id.name,
                                                                                      code=line.mobile_python_code),
                                                         'type': 'url',
                                                         'is_general_map': True,
                                                         'field_ref_doc_id': field_ref_doc.id,
                                                         'field_ref_doc_model': field_ref_doc._name,
                                                         'approve_setting_id': field_ref_doc_setting.id,
                                                         })
                        if not can_url:
                            display_list.append({'key': line.show_name or line.field_id.field_description,
                                                 'value': self.get_field_vale(record, line.field_id.name,
                                                                              code=line.mobile_python_code),
                                                 'type': 'text'})


                    elif line.mobile_display_type in ('accordionList', 'groupText'):
                        group_num += 1
                        group_dict = {'key': line.show_name,
                                      'type': line.mobile_display_type}
                        content = []
                # 当存在组元素后
                else:
                    if line.mobile_display_type in ('text', 'accordionText'):
                        content.append({'key': line.show_name or line.field_id.field_description,
                                        'value': self.get_field_vale(record, line.field_id.name,
                                                                     code=line.mobile_python_code),
                                        'type': line.mobile_display_type})
                    elif line.mobile_display_type == 'url':
                        can_url = False
                        # 如果字段为many2one 才提供 连接，否则按text
                        if not get_url_doc and line.field_id.ttype == 'many2one':
                            field_ref_doc = getattr(record, line.field_id.name)
                            if field_ref_doc:
                                field_ref_doc_type = self.env['workflow.task'].get_approve_document_type(approve_document=field_ref_doc)
                                field_ref_doc_setting = field_ref_doc.get_mapp_approve_setting(type=field_ref_doc_type)
                                # 仅当 字段的模型 有配置对应的 移动审批配置，才可点开
                                if field_ref_doc_setting:
                                    can_url = True
                                    content.append({'key': line.show_name or line.field_id.field_description,
                                                         'value': self.get_field_vale(record, line.field_id.name,
                                                                                      code=line.mobile_python_code),
                                                         'type': 'url',
                                                         'is_general_map': True,
                                                         'field_ref_doc_id': field_ref_doc.id,
                                                         'field_ref_doc_model': field_ref_doc._name,
                                                         'approve_setting_id': field_ref_doc_setting.id,
                                                         })

                        if not can_url:
                            content.append({'key': line.show_name or line.field_id.field_description,
                                             'value': self.get_field_vale(record, line.field_id.name,
                                                                          code=line.mobile_python_code),
                                             'type': 'text'})
                    elif line.mobile_display_type in ('accordionList', 'groupText'):
                        group_num += 1
                        ref_content = content
                        group_dict['content'] = ref_content
                        ref_dict = group_dict
                        # 将上 一个组  加入到displaylist
                        display_list.append(ref_dict)
                        # 创建新的组
                        group_dict = {'key': line.show_name,
                                      'type': line.mobile_display_type}
                        content = []

            if group_num and content:
                ref_content = content
                group_dict['content'] = content
                ref_dict = group_dict
                # 将上 最后一个组  加入到displaylist
                display_list.append(ref_dict)
        # 获取行信息
        if approve_setting.line_field_ids:
            for line in approve_setting.line_field_ids:
                if line.line_info_ids:
                    child_display_lines = line.line_info_ids.filtered(lambda r: r.line_display)
                    if not child_display_lines:
                        continue
                    table_head = []
                    table_body = []
                    field_bold_flag = []
                    field_red_flag = []
                    line_field = line.line_field_id.name
                    for detail in child_display_lines:
                        table_head.append(detail.show_name or detail.field_id.field_description or 'undefind')
                        field_red_flag.append(detail.red_flag)
                        field_bold_flag.append(detail.bold_flag)
                    display_style_dict.update({
                        line.relation_model_id.model: {
                            'bold_flag': [index for index,flag in enumerate(field_bold_flag) if flag],
                            'red_flag': [index for index,flag in enumerate(field_red_flag) if flag]
                        }
                    })
                    # has_currency = self.check_has_currency(line.line_field_id.model_id.model)
                    for line_record in getattr(record, line_field):
                        table_body.append({  # 'currency': has_currency and self.get_currency(line_record) or '',
                            'line_id': line_record.id,
                            'line_type': line_field,
                            'model': line_record._name,
                            'task_id': not get_url_doc and task_id or 0,
                            'get_url_doc': get_url_doc,
                            # 'head_model': record._name,
                            # 'head_id': record.id,
                            'extensible': line.extensible,
                            'cell': [
                                self.get_field_vale(line_record, r.field_id.name, code=r.mobile_python_code or None,
                                                    type=r.mobile_display_type, number=number,
                                                    filename_field=r.attachment_field_id.name)
                                for r, number in zip(child_display_lines, range(1, len(child_display_lines) + 1))]})
                    table_dict = {'head': table_head,
                                  'body': table_body}
                    display_list.append({'key': line.line_title,
                                         'content': table_dict,
                                         'type': 'table',
                                         'extensible': line.extensible})
        # if get_url_doc:
        #     return {'is_general_map': True,
        #             'display_field': display_list}
        return {'is_general_map': True,
                'display_field': display_list,
                'display_field_style': display_style_dict,
                'map_approve_type': type,
                'document_type': approve_setting.card_name_py and self.get_field_vale(record, False,
                                                                                      code=approve_setting.card_name_py,
                                                                                      special=True) or approve_setting.card_name or ''}

    @api.multi
    def document_info_line(self, line_id, line_type=None, line_record=None, approve_setting=None, get_url_doc=False):
        lov_field = line_type
        if not line_record:
            self.ensure_one()
            record = self
            type = self.env.context.get('map_approve_type', False)
            approve_setting = approve_setting or self.get_mapp_approve_setting(type=type)
            line_record = getattr(record, lov_field).filtered(lambda r: r.id == line_id)
        else:
            line_record = line_record

        if not lov_field:
            return {}
        child_line = approve_setting.line_field_ids.filtered(lambda r: r.line_field_id.name == lov_field)
        if not child_line:
            return {}
        child_line = child_line[0]
        display_list = []
        for line, number in zip(child_line.line_info_ids, range(1, len(child_line.line_info_ids) + 1)):
            can_url = False
            if line.mobile_display_type == 'url' and line.field_id.ttype == 'many2one' and not get_url_doc:
                # 如果字段为many2one 才提供 连接，否则按text
                field_ref_doc = getattr(line_record, line.field_id.name)
                if field_ref_doc:
                    field_ref_doc_type = self.env['workflow.task'].get_approve_document_type(
                        approve_document=field_ref_doc)
                    field_ref_doc_setting = field_ref_doc.get_mapp_approve_setting(type=field_ref_doc_type)
                    # 仅当 字段的模型 有配置对应的 移动审批配置，才可点开
                    if field_ref_doc_setting:
                        can_url = True
                        display_list.append({'key': line.show_name or line.field_id.field_description,
                                        'value': self.get_field_vale(line_record, line.field_id.name,
                                                                     code=line.mobile_python_code or None,
                                                                     type=line.mobile_display_type, number=number,
                                                                     filename_field=line.attachment_field_id.name),
                                        'type': 'url',
                                        'is_general_map': True,
                                        'field_ref_doc_id': field_ref_doc.id,
                                        'field_ref_doc_model': field_ref_doc._name,
                                        'approve_setting_id': field_ref_doc_setting.id,
                                        'bold_flag': line.bold_flag,
                                        'red_flag': line.red_flag,
                                        })
            if not can_url:
                display_list.append({'key': line.show_name or line.field_id.field_description,
                                     'value': self.get_field_vale(line_record, line.field_id.name,
                                                                  code=line.mobile_python_code or None,
                                                                  type=line.mobile_display_type, number=number,
                                                                  filename_field=line.attachment_field_id.name),
                                     'type': line.mobile_display_type,
                                     'bold_flag': line.bold_flag,
                                     'red_flag':line.red_flag,
                                     })
        if get_url_doc:
            return {'display_field': display_list,
                    'model': line_record._name,
                    'line_detail_title': child_line.line_title or child_line.line_field_id.field_description}
        # has_currency = self.check_has_currency(lov_field.model_id.model)
        return {'is_general_map': True,
                'display_field': display_list,
                'model': line_record._name,
                'map_approve_type': type,
                # 'crrency': has_currency and self.get_currency(line_record) or '',
                'line_detail_title': child_line.line_title or child_line.line_field_id.field_description}

    # 提供一个 移动审批页面 字段 类型 为url的，链接到关联的 移动审批的功能--
    # todo 仅适用于配置的移动审批，原手工开发  不支持此功能
    @api.model
    def get_url_document_info(self, args):
        lang = args.get('lang', 'zh_CN')
        self = self.suspend_security().with_context(lang=lang)
        approve_setting = self.env['map.approve.setting'].browse(args.get('approve_setting_id'))
        record = self.env[args.get('record_model')].browse(args.get('record_id'))
        result = self.document_info(0, open_record=record, approve_setting=approve_setting, get_url_doc=True)
        return result

    @api.model
    def get_url_document_info_line(self, args):
        lang = args.get('lang', 'zh_CN')
        self = self.suspend_security().with_context(lang=lang)
        approve_setting = self.env['map.approve.setting'].browse(args.get('approve_setting_id'))
        line_record = self.env[args.get('line_model')].browse(args.get('line_id'))
        line_type = args.get('line_type')
        result = self.document_info_line(args.get('line_id'),line_record=line_record ,line_type=line_type, approve_setting=approve_setting,
                                           get_url_doc=True)
        return result



    @api.multi
    def get_attachments(self):
        if hasattr(self, 'attachment_ids'):
            return self.attachment_ids
        elif hasattr(self, 'attachment'):
            return self.attachment
        elif hasattr(self, 'attachments'):
            return self.attachments
        elif hasattr(self, 'attachment_id'):
            return self.attachment_id

    @api.model
    # @tools.ormcache('self._name', 'type')
    def get_mapp_approve_setting(self, type=None):
        domain = [('ref_model_id.model', '=', self._name), ('usage', '=', 'map')]
        if type:
            domain += ['|','&',('specified_document_type','=','single'),('type_ref_id', '=', type),
                       '&',('specified_document_type','=','multiple'),('type_ref_ids.type_ref_id', '=', type)]

        approve_setting = self.env["map.approve.setting"].search(domain, limit=1)
        if not approve_setting:
            approve_setting = self.env["map.approve.setting"].search(
                [('ref_model_id.model', '=', self._name), ('usage', '=', 'map'),('specified_document_type','=', False)], limit=1)
        if not approve_setting:
            raise ValidationError(_(
                'Please contact the administrator to configure the map approve setting for the current model %s!') % self._name)
        return approve_setting

    @api.model
    def get_field_vale(self, record, field, code=None, special=False, type=None, number=0, filename_field=None):
        lang = self.env.context.get('lang', False) or self.env.user.lang
        if type == 'lineNumber':
            return number
        elif type == 'attachment':
            return self.get_line_attachment(record, field, filename_field)

        if not code:
            if field:
                return self.get_field_export_value(record, field)
            else:
                return ''
        else:
            eval_context = {'record': record,
                            'today': fields.Date.context_today(self),
                            'now': fields.Datetime.context_timestamp(self, datetime.datetime.now()),
                            'self': self,
                            'fields': fields,
                            'time': time,
                            'datetime': datetime,
                            'dateutil': dateutil,
                            'timezone': timezone,
                            'user': self.env.user,
                            'result': ''}
            try:
                safe_eval("result =" + code.strip(), eval_context, mode='exec', nocopy=True)
            except Exception as e:
                if special:
                    return ''
                if field:
                    return self.get_field_export_value(record, field)
                else:
                    return "error: %s" % code.strip()
            return eval_context['result'] or ''

    @api.model
    def get_line_attachment(self, line_record, field, filename_field):
        file_name = getattr(line_record, filename_field, False)
        if not file_name:
            return {'url': '',
                    'name': '',
                    'type': 'attachment',
                    'id': ''}
        base_url = self.env['ir.config_parameter'].get_web_base_url_out() + '/web/content'
        special_str = '?model=%s&field=%s&id=%s&download=true&filename_field=%s&database=%s&skip=1&download=true' % (
            line_record._name, field, line_record.id, filename_field, self.env.cr.dbname)
        result = {'url': (base_url + special_str),
                  'name': getattr(line_record, filename_field, u'') or u'',
                  'type': 'attachment',
                  'id': line_record.id}
        return result

    @api.model
    def get_currency_amount(self, record, field):
        currency_field = getattr(record._fields[field], 'currency_field', False)
        amount = format_amount(getattr(record, field, 0.00)) or ''
        if currency_field:
            currency_record = getattr(record, currency_field)
            if currency_record.suspend_security().position == 'after':
                return amount + (currency_record.symbol or '')
            else:
                return (currency_record.symbol or '') + amount
        else:
            return amount

    @api.model
    def check_has_currency(self, model):
        currency_fields = self.env['ir.model.fields'].search([('ttype', '=', 'many2one'),
                                                              ('relation', '=', 'res.currency'),
                                                              ('model_id.model', '=', model)])
        return currency_fields and True or False

    @api.model
    def get_currency(self, record):
        currency_fields = self.env['ir.model.fields'].search([('ttype', '=', 'many2one'),
                                                              ('relation', '=', 'res.currency'),
                                                              ('model_id.model', '=', record._name)])
        if len(currency_fields) >= 1:
            local_currency_field = currency_fields.filtered(
                lambda r: r.name in ('currency_id', 'x_currency_id', 'x_currency'))
            if local_currency_field:
                return getattr(record, local_currency_field[0].name).symbol or ''
            else:
                return getattr(record, currency_fields[0].name).symbol or ''
        else:
            return ''

    #######  移动端审批通用 方法  #######

    @api.multi
    def _compute_mysterious_figures(self):
        for rec in self:
            rec.mysterious_figures += 1
            sql = '''update %s set display_type=%s WHERE id=%d AND display_type!=%s''' % (rec._table, "'tree'", rec.id, "'tree'")
            self._cr.execute(sql)
            # rec.onchange_mysterious_figures()

    @api.multi
    def onchange_mysterious_figures(self):
        self = self.suspend_security()
        for rec in self:
            if rec.display_type != 'tree':
                rec.suspend_security().write({'display_type': 'tree'})

    @api.multi
    def view_diagram(self):
        self = self.suspend_security()
        form_view = self.env.ref('workflow.view_dialog_form')
        ctx = self.env.context.copy()
        ctx.update({
            'workflow_id': self.workflow_id.id or self.get_create_workflow_id(self._name),
            'document_model': self._name,
            'document_id': self.id,
        })
        act = {
            'name': _t(self.env.lang, ['Workflows', u'工作流']),
            'type': 'ir.actions.act_window',
            'res_model': 'dialog',
            'view_type': 'form',
            # 'views': [(form_view.id, 'form')],
            'view_mode': 'form',
            'view_id': form_view.id,
            'target': 'new',
            'context': ctx
        }

        return act
        # for rec in self:
        #     rec.write({'display_type': 'diagram'})

    @api.multi
    def view_tree(self):
        self = self.suspend_security()
        for rec in self:
            rec.write({'display_type': 'tree'})

    @api.multi
    def _compute_display_type(self):
        for rec in self:
            rec.display_type = 'tree'

    # 用于创建已保存未提交单据
    @api.model
    def create(self,vals):
        saved_document_obj = self.env['workflow.saved.document']
        res = super(WorkflowInterface, self).create(vals)
        if res._name != 'workflow.task' and not self.env.context.get('skip_saved_document', False):
            doc_vals = {'document': res._name + ',' + str(res.id),
                        'model_id': self.env['ir.model'].search([('model', '=', res._name)],limit=1).id,
                        'document_name_char_store': res.with_context(for_task=True, skip=True, lang=self.env.context.get('lang', 'en_US')).name_get()[0][1] or 'N/A'
            }
            saved_document_obj.create(doc_vals)
        return res


    @api.multi
    def _compute_workflow_id(self):
        workflow_instance_obj = self.env['workflow.instance']
        for rec in self:
            rec.workflow_id = False
            workflow_instance = self.env['workflow.instance'].search(
                [('approve_document', '=', self._name + ',' + str(rec.id)),
                 ('state', '!=', 'withdrawn')], limit=1)
            if workflow_instance:
                rec.workflow_id = workflow_instance.workflow_id.id
            else:
                rec.workflow_id = self.get_create_workflow_id(self._name, rec.id)

    from odoo.tools.misc import profile as graph_profile
    from odoo.addons.workflow.tools.profiler import profile as log_profile
    # @graph_profile('/opt/odoo/1.profile')
    @api.model
    def get_create_workflow_id(self, model, res_id=None):
        self = self.with_context(prefetch_fields=False)
        base_workflow_obj = self.env['workflow']
        if not res_id:
            wkf_objs = self.env['workflow'].with_context(forced_auto_join=True).search(
                [('wkf_model_id.model', '=', model), ('active', '=', True),('auto_inactive','=',False),('template','=',False)], order='sequence', limit=1)
            return wkf_objs.id
        wkfs = base_workflow_obj.with_context(forced_auto_join=True).search(
            [('wkf_model_id.model', '=', model), ('active', '=', True),('auto_inactive','=',False),('template','=',False)], order='sequence')
        wkf_obj = base_workflow_obj
        for wkf in wkfs:
            if not wkf.wkf_condition_id or (wkf.wkf_condition_id and wkf.wkf_condition_id.check_match_domain(model, res_id) is True):
                wkf_obj = wkf
                break
            else:
                pass
        if not wkf_obj:
            pass
            # raise ValidationError(_('There isn’t active workflow, please contact the sysadmin.'))
        return wkf_obj.id

    @api.multi
    def get_formview_action(self):
        if hasattr(self, 'open_document') and self.open_document():
            return self.open_document()
        view_id = self.get_formview_id()
        return {
            'type': 'ir.actions.act_window',
            'res_model': self._name,
            'view_type': 'form',
            'view_mode': 'form',
            'views': [(view_id, 'form')],
            'target': 'current',
            'res_id': self.id,
            'context': dict(self._context),
        }

    # 用于消息界面获取各类单据视图
    @api.multi
    def get_formview_id(self):
        if self._name == 'kthrp.stock.requisition.order':
            if self.requisition_type_id.requisition_page_id.view_id:
                view_id = self.requisition_type_id.requisition_page_id.view_id.id
        elif self._name == 'kthrp.purchase.requisition':
            if self.requisition_purchase_type_id.requisition_page_id.view_id:
                view_id = self.requisition_purchase_type_id.requisition_page_id.view_id.id
        elif self._name == 'kthrp.payable.payment.request':
            if self.type_id.page_id.view_id:
                view_id = self.type_id.page_id.view_id.id
        elif self._name == 'kthrp.asset.sundry.request':
            if self.type_id.page_id.view_id:
                view_id = self.type_id.page_id.view_id.id
        elif self._name == 'kthrp.contract.contract':
            if self.suspend_security().type_id.contract_page_id.view_id:
                view_id = self.suspend_security().type_id.contract_page_id.view_id.id
        else:
            view_id = super(WorkflowInterface, self).get_formview_id()
        return view_id

    @api.multi
    @api.depends('submit_time')
    def _compute_submit_date(self):
        for rec in self:
            if rec.submit_time:
                rec.submit_date = rec.submit_time[:10]

    # 审批节点审批前调用
    '''
        workflow_id:工作流对象
        approve_document:审批的具体数据对象
        current_node_instance:当前节点
    '''

    @api.multi
    def workflow_task_approve(self, workflow_id=False, approve_document=False, current_node_instance=False):
        pass

    # 工作流审批后调用
    @api.multi
    def workflow_approved(self):
        pass

    # 工作流拒绝后调用
    @api.multi
    def workflow_rejected(self):
        pass

    @api.multi
    def open_document(self):
        pass

    @api.multi
    @api.depends('submit_user_id')
    def _compute_submit_employee(self):
        for rec in self:
            if rec.submit_user_id and rec.submit_user_id.related_employee_id:
                rec.submit_employee_id = rec.submit_user_id.related_employee_id.id

    @api.multi
    @api.depends('has_approve')
    def _compute_delay_display(self):
        time.sleep(0.01)
        for rec in self:
            rec.delay_display = True

    @api.one
    def _compute_has_approve(self):
        wkf_instance_obj = self.env['workflow.instance']
        wkf_instance = wkf_instance_obj.search([('approve_document', '=', self._name + ',' + str(self.id)),
                                                ('state', '=', 'processing')])
        wkf_instance_len = len(wkf_instance)
        if len(wkf_instance) == 1:
            activity_instance = wkf_instance.instance_line_ids.filtered(lambda line: line.state == 'processing')
            signals_type_code = activity_instance.workflow_line_id.signal_type_ids.mapped('code')
            if 'approve' in signals_type_code:
                self.has_approve = True
        elif wkf_instance_len != 0:
            raise ValidationError(_('Processing workflow instance is not unique!'))

    @api.one
    def _compute_has_reject(self):
        wkf_instance_obj = self.env['workflow.instance']
        wkf_instance = wkf_instance_obj.search([('approve_document', '=', self._name + ',' + str(self.id)),
                                                ('state', '=', 'processing')])
        wkf_instance_len = len(wkf_instance)
        if len(wkf_instance) == 1:
            activity_instance = wkf_instance.instance_line_ids.filtered(lambda line: line.state == 'processing')
            signals_type_code = activity_instance.workflow_line_id.signal_type_ids.mapped('code')
            if 'reject' in signals_type_code:
                self.has_reject = True
        elif wkf_instance_len != 0:
            raise ValidationError(_('Processing workflow instance is not unique!'))

    @api.one
    @api.depends('approve_task_id')
    def _compute_has_transfer(self):
        wkf_instance_obj = self.env['workflow.instance']
        wkf_instance = wkf_instance_obj.search([('approve_document', '=', self._name + ',' + str(self.id)),
                                                ('state', '=', 'processing')])
        wkf_instance_len = len(wkf_instance)
        if len(wkf_instance) == 1:
            activity_instance = wkf_instance.instance_line_ids.filtered(lambda line: line.state == 'processing')
            signals_type_code = activity_instance.workflow_line_id.signal_type_ids.mapped('code')
            if 'transfer' in signals_type_code:
                # self.has_transfer = True and not self.approve_task_id.is_special_transfer
                if self.approve_task_id.from_task_id.approve_result_id.code in ('add', 'linkup'):
                    self.has_transfer = False
                else:
                    self.has_transfer = True
        elif wkf_instance_len != 0:
            raise ValidationError(_('Processing workflow instance is not unique!'))

    @api.one
    @api.depends('approve_task_id')
    def _compute_has_linkup(self):
        wkf_instance_obj = self.env['workflow.instance']
        wkf_instance = wkf_instance_obj.search([('approve_document', '=', self._name + ',' + str(self.id)),
                                                ('state', '=', 'processing')])
        wkf_instance_len = len(wkf_instance)
        if len(wkf_instance) == 1:
            activity_instance = wkf_instance.instance_line_ids.filtered(lambda line: line.state == 'processing')
            signals_type_code = activity_instance.workflow_line_id.signal_type_ids.mapped('code')
            if 'linkup' in signals_type_code:
                # self.has_transfer = True and not self.approve_task_id.is_special_transfer
                if self.approve_task_id.from_task_id.approve_result_id.code in ('add', 'linkup'):
                    self.has_linkup = False
                else:
                    self.has_linkup = True
        elif wkf_instance_len != 0:
            raise ValidationError(_('Processing workflow instance is not unique!'))

    @api.one
    @api.depends('approve_task_id')
    def _compute_has_reply(self):
        wkf_instance_obj = self.env['workflow.instance']
        wkf_instance = wkf_instance_obj.search([('approve_document', '=', self._name + ',' + str(self.id)),
                                                ('state', '=', 'processing')])
        wkf_instance_len = len(wkf_instance)
        if len(wkf_instance) == 1:
            activity_instance = wkf_instance.instance_line_ids.filtered(lambda line: line.state == 'processing')
            signals_type_code = activity_instance.workflow_line_id.signal_type_ids.mapped('code')
            if 'linkup' in signals_type_code:
                if self.approve_task_id.from_task_id.approve_result_id.code == 'linkup':
                    self.has_reply = False
                else:
                    self.has_reply = True
            else:
                self.has_reply = True
        elif wkf_instance_len != 0:
            raise ValidationError(_('Processing workflow instance is not unique!'))

    @api.one
    @api.depends('approve_task_id')
    def _compute_has_add(self):
        wkf_instance_obj = self.env['workflow.instance']
        wkf_instance = wkf_instance_obj.search([('approve_document', '=', self._name + ',' + str(self.id)),
                                                ('state', '=', 'processing')])
        wkf_instance_len = len(wkf_instance)
        if len(wkf_instance) == 1:
            activity_instance = wkf_instance.instance_line_ids.filtered(lambda line: line.state == 'processing')
            signals_type_code = activity_instance.workflow_line_id.signal_type_ids.mapped('code')
            if 'add' in signals_type_code:
                # self.has_transfer = True and not self.approve_task_id.is_special_transfer
                if self.approve_task_id.from_task_id.approve_result_id.code in ('add', 'linkup'):
                    self.has_add = False
                else:
                    self.has_add = True
        elif wkf_instance_len != 0:
            raise ValidationError(_('Processing workflow instance is not unique!'))


    @api.multi
    def get_has_transfer(self, task):
        self.ensure_one()
        wkf_instance_obj = self.env['workflow.instance']
        wkf_instance = wkf_instance_obj.search([('approve_document', '=', self._name + ',' + str(self.id)),
                                                ('state', '=', 'processing')])
        wkf_instance_len = len(wkf_instance)
        if len(wkf_instance) == 1:
            activity_instance = wkf_instance.instance_line_ids.filtered(lambda line: line.state == 'processing')
            signals_type_code = activity_instance.workflow_line_id.signal_type_ids.mapped('code')
            if 'transfer' in signals_type_code:
                if 'transfer' in signals_type_code:
                    # result = True and not task.is_special_transfer
                    if task.from_task_id.approve_result_id.code in ('add', 'linkup'):
                        result = False
                    else:
                        result= True
                    return result
                else:
                    return False
            else:
                return False
        elif wkf_instance_len != 0:
            return False

    @api.one
    def _compute_has_return(self):
        wkf_instance_obj = self.env['workflow.instance']
        wkf_instance = wkf_instance_obj.search([('approve_document', '=', self._name + ',' + str(self.id)),
                                                ('state', '=', 'processing')])
        wkf_instance_len = len(wkf_instance)
        if len(wkf_instance) == 1:
            activity_instance = wkf_instance.instance_line_ids.filtered(lambda line: line.state == 'processing')
            signals_type_code = activity_instance.workflow_line_id.signal_type_ids.mapped('code')
            if 'return' in signals_type_code:
                if self.approve_task_id.from_task_id.approve_result_id.code in ('add', 'linkup'):
                    self.has_return = False
                else:
                    self.has_return = True
        elif wkf_instance_len != 0:
            raise ValidationError(_('Processing workflow instance is not unique!'))

    @api.multi
    def get_has_return(self, task):
        self.ensure_one()
        wkf_instance_obj = self.env['workflow.instance']
        wkf_instance = wkf_instance_obj.search([('approve_document', '=', self._name + ',' + str(self.id)),
                                                ('state', '=', 'processing')])
        wkf_instance_len = len(wkf_instance)
        if wkf_instance_len == 1:
            activity_instance = wkf_instance.instance_line_ids.filtered(lambda line: line.state == 'processing')
            signals_type_code = activity_instance.workflow_line_id.signal_type_ids.mapped('code')
            if 'return' in signals_type_code:
                if task.from_task_id.approve_result_id.code in ('add', 'linkup'):
                    return False
                else:
                    return True
            else:
                return False
        else:
            return False

    @api.multi
    def get_has_add(self, task):
        self.ensure_one()
        wkf_instance_obj = self.env['workflow.instance']
        wkf_instance = wkf_instance_obj.search([('approve_document', '=', self._name + ',' + str(self.id)),
                                                ('state', '=', 'processing')])
        wkf_instance_len = len(wkf_instance)
        if wkf_instance_len == 1:
            activity_instance = wkf_instance.instance_line_ids.filtered(lambda line: line.state == 'processing')
            signals_type_code = activity_instance.workflow_line_id.signal_type_ids.mapped('code')
            if 'add' in signals_type_code:
                if task.from_task_id.approve_result_id.code in ('add', 'linkup'):
                    return False
                else:
                    return True
            else:
                return False
        else:
            return False

    @api.multi
    def get_has_linkup(self, task):
        self.ensure_one()
        wkf_instance_obj = self.env['workflow.instance']
        wkf_instance = wkf_instance_obj.search([('approve_document', '=', self._name + ',' + str(self.id)),
                                                ('state', '=', 'processing')])
        wkf_instance_len = len(wkf_instance)
        if wkf_instance_len == 1:
            activity_instance = wkf_instance.instance_line_ids.filtered(lambda line: line.state == 'processing')
            signals_type_code = activity_instance.workflow_line_id.signal_type_ids.mapped('code')
            if 'linkup' in signals_type_code:
                if task.from_task_id.approve_result_id.code in ('add', 'linkup'):
                    return False
                else:
                    return True
            else:
                return False
        else:
            return False


    # 北京友谊重写
    @api.one
    def _compute_has_withdraw(self):
        wkf_instance_obj = self.env['workflow.instance']
        wkf_instance = wkf_instance_obj.search([('approve_document', '=', self._name + ',' + str(self.id)),
                                                ('state', '=', 'processing')])
        wkf_instance_len = len(wkf_instance)
        if len(wkf_instance) == 1:
            if self._name == 'kthrp.hr.employment.contract':
                if (wkf_instance.workflow_id.withdraw and (self.submit_user_id.id == self.env.user.id)) and (
                        self.approval_state == 'submitted' or 'withdraw' in wkf_instance.instance_line_ids.filtered(
                        lambda x: x.state == 'processing').workflow_line_id.signal_type_ids.mapped('code')):
                    self.has_withdraw = True
                else:
                    setting = self.env.ref('workflow.workflow_default_setting', raise_if_not_found=False)
                    if setting and setting.allow_withdraw_before_next_approve and wkf_instance.workflow_id.withdraw and self.approval_state == 'approving':
                        current_node = wkf_instance.instance_line_ids.filtered(
                            lambda x: x.state == 'processing').workflow_line_id
                        current_node_instance = wkf_instance.instance_line_ids.filtered(
                            lambda x: x.state == 'processing')
                        if current_node_instance and not current_node.countersign and 'withdraw' in current_node.signal_type_ids.mapped('code'):
                            last_current_node_instance = self.env[
                                'workflow.instance.line.transition'].suspend_security().search(
                                [('to_instance_line_id', '=', current_node_instance.id)], limit=1).from_instance_line_id
                            if last_current_node_instance and not last_current_node_instance.workflow_line_id.countersign and last_current_node_instance.task_ids.filtered(lambda x: x.approve_result_id.code == 'approve' and x.actual_user_id.id == self.env.user.id):
                                self.has_withdraw = True

            else:
                if (wkf_instance.workflow_id.withdraw and (self.submit_user_id.id == self.env.user.id)) and (
                        self.state == 'submitted' or 'withdraw' in wkf_instance.instance_line_ids.filtered(
                        lambda x: x.state == 'processing').workflow_line_id.signal_type_ids.mapped('code')):
                    self.has_withdraw = True
                else:
                    setting = self.env.ref('workflow.workflow_default_setting', raise_if_not_found=False)
                    if setting and setting.allow_withdraw_before_next_approve and wkf_instance.workflow_id.withdraw and self.state == 'approving':
                        current_node = wkf_instance.instance_line_ids.filtered(
                            lambda x: x.state == 'processing').workflow_line_id
                        current_node_instance = wkf_instance.instance_line_ids.filtered(
                            lambda x: x.state == 'processing')
                        if current_node_instance and not current_node.countersign and 'withdraw' in current_node.signal_type_ids.mapped('code'):
                            last_current_node_instance = self.env[
                                'workflow.instance.line.transition'].suspend_security().search(
                                [('to_instance_line_id', '=', current_node_instance.id)], limit=1).from_instance_line_id
                            if last_current_node_instance and not last_current_node_instance.workflow_line_id.countersign and last_current_node_instance.task_ids.filtered(lambda x: x.approve_result_id.code == 'approve' and x.actual_user_id.id == self.env.user.id):
                                self.has_withdraw = True
        elif wkf_instance_len != 0:
            raise ValidationError(_('Processing workflow instance is not unique!'))

    @api.model
    def get_model_id(self, model):
        self.env.cr.execute("select id from ir_model where model='%s' limit 1;"%model)
        model_id = self.env.cr.fetchone()
        return model_id and model_id[0] or 0


    # 获取单据审批进度
    @api.one
    def _get_wkf_task(self):
        self.env.cr.execute("""select wt.id from workflow_task wt,
                                                           ir_model im
                                                           where wt.wkf_model_id = im.id
                                                           and im.model='%s'
                                                           and wt.model_instance_id = %s GROUP BY wt.id order by wt.id""" % (self._name, str(self.id)))

        result = self.env.cr.fetchall() or []
        if result:
            task_ids = []
            for task_id in result:
                if task_id:
                    task_ids.append(task_id[0] or 0)
            self.task_ids = task_ids

        # model_id = self.get_model_id(self._name)
        # if model_id:
        #     tasks = self.env['workflow.task'].search([('wkf_model_id', '=', model_id),
        #                                                          ('model_instance_id', '=', self.id)])
        #     self.task_ids = tasks.ids

    @api.one
    @api.depends('task_ids')
    def _get_approve_task(self):
        if self.task_ids and self.env.user.related_employee_id:
            employee = self.env.user.related_employee_id
            self.env.cr.execute("""select wt.id from workflow_task wt,
                                                                       ir_model im
                                                                       where wt.wkf_model_id = im.id
                                                                       and im.model='%s'
                                                                       and wt.model_instance_id = %s
                                                                       and wt.allocated_approver_id = %s
                                                                       and wt.wkf_line_instance_state = 'processing'
                                                                       and wt.approve_result_id is null
                                                                       order by wt.id limit 1 """ % (
            self._name, str(self.id), str(employee.id)))
            task_id = self.env.cr.fetchone()
            task_id = task_id and task_id[0] or []
            if task_id:
                self.approve_task_id = task_id

            # model_id = self.get_model_id(self._name)
            # task = self.task_ids.search(
            #     [('approve_result_id', '=', False), ('allocated_approver_id', '=', employee.id),
            #      ('wkf_model_id', '=', model_id),
            #      ('model_instance_id', '=', self.id), ('wkf_line_instance_state', '=', 'processing')])
            # if len(task) == 1:
            #     self.approve_task_id = task.id
            # elif task:
            #     self.approve_task_id = task[0].id

    @api.multi
    def action_to_draft(self):
        self.ensure_one()
        workflow_instances = self.env['workflow.instance'].search(
            [('approve_document', '=', self._name + ',' + str(self.id)),
             ('state', '=', 'rejected')])
        for workflow_instance in workflow_instances:
            tasks = workflow_instance.instance_line_ids.mapped('task_ids').filtered(
                lambda x: x.approve_result_id.code == 'reject')
            if tasks:
                tasks.write({'reject_display': False})
        saved_document_obj = self.env['workflow.saved.document']
        saved_document_obj.active_saved_document(self._name, self.id)
        return True

    @api.multi
    def action_approve_wizard(self):
        view = self.env.ref('workflow.view_workflow_approve_form')
        return {
            'name': _('Approve.'),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.approve',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new'
        }

    @api.multi
    def action_reject_wizard(self):
        view = self.env.ref('workflow.view_workflow_reject_form')
        return {
            'name': _('Reject.'),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.reject',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
        }

    @api.multi
    def action_reply_wizard(self):
        view = self.env.ref('workflow.view_workflow_reply_form')
        return {
            'name': _('Reply.'),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.approve',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new'
        }

    @api.multi
    def action_transfer_wizard(self):
        view = self.env.ref('workflow.view_workflow_transfer_form')
        lang = self.env.context.get('lang', '')
        return {
            'name': _t(lang, ['Transfer', u'转办']),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.transfer',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
        }

    @api.multi
    def action_linkup_wizard(self):
        view = self.env.ref('workflow.view_workflow_linkup_form')
        lang = self.env.context.get('lang', '')
        return {
            'name': _t(lang, ['Linkup', u'沟通']),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.linkup',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
        }

    @api.multi
    def action_add_wizard(self):
        view = self.env.ref('workflow.view_workflow_add_form')
        lang = self.env.context.get('lang', '')
        return {
            'name': _t(lang, ['Add', u'加签']),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.add',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
        }


    @api.multi
    def action_return_wizard(self):
        view = self.env.ref('workflow.view_workflow_return_form')
        return {
            'name': _('Return.'),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.return',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
        }

    def change_reminder_times(self,res):
        pass

    @api.multi
    def action_reminders_wizard(self):
        """催办动作"""
        for res in self:
            self.change_reminder_times(res)
        current_reminders_time = datetime.datetime.now()
        if not self.last_reminders_time or (
            current_reminders_time - datetime.datetime.strptime(self.last_reminders_time,
                                                                '%Y-%m-%d %H:%M:%S')).total_seconds() > 900:
            # self.last_reminders_time = current_reminders_time
            # 发送消息
            message = self.env['message'].search([('trigger_type', '=', 'button_clicked'),
                                                             ('button_method', '=', 'action_reminders_wizard')],
                                                            limit=1)
            if message:
                tasks = self.task_ids.filtered(lambda task: task.wkf_line_instance_state == "processing" and task.allocated_approver_id and not task.approve_result_id)
                self.last_reminders_time = current_reminders_time
                for x in tasks:
                    if x.had_reminded:
                        x.had_reminded -= 1
                    else:
                        x.had_reminded = 100
                # if tasks:
                #     tasks.write({'had_reminded': 1})
                message.with_delay().send(self.id, self.env.lang, real_model_name=self._name)
                return {
                    'type': 'ir.actions.client',
                    'target': 'new',
                    'tag': 'action_notification',
                    'title': _('Success'),
                    'message': _('Reminder Success'),
                    'tip_type': 'notification',
                    'sticky': False,
                    # }
                }
            else:
                raise ValidationError(_("Failed to send reminders. Please contact your system administrator to check the configuration of the messaging platform."))

        else:
            raise ValidationError(_("The time interval between two reminders should not be less than 15 minutes!"))

    @api.multi
    def action_withdraw_wizard(self):
        view = self.env.ref('workflow.view_workflow_withdraw_form')
        context = self.env.context.copy()
        context.update({
            'has_wizard': True,
            'dialog_default_collapse': True
        })
        return {
            'name': _('Withdraw.'),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.withdraw',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
            'context': context
        }

    def get_wkf_instance(self, wkf_instance):
        pass

    # 提交人收回 True，上一审批人收回，False
    def get_withdraw_method_flag(self):
        self = self.suspend_security()
        method_flag = False
        last_approver_ids = []
        if self.task_ids:
            model = self.env['ir.model'].search([('model', '=', self._name)], limit=1)
            task = self.task_ids.search([('approve_result_id', '=', False), ('wkf_model_id', '=', model.id),
                                         ('model_instance_id', '=', self.id), ('wkf_line_instance_state', '=', 'processing')])
            if task:
                last_task = task[0].last_wkf_line_instance_id.task_ids.filtered(lambda x: x.approve_result_id.code == 'approve')
                last_approver_ids = last_task.mapped('actual_user_id').ids if last_task else []
        if self.submit_user_id.id == self.env.user.id and self.submit_user_id.id not in last_approver_ids:
            method_flag = True
        return method_flag

    @api.multi
    def action_withdraw(self):
        self.ensure_one()
        # 单据修改状态时，可能会触发其他数据的计算字段的回写，导致权限报错；添加权限跳跃
        self = self.suspend_security()
        self = self.with_context(active_id=self.id, active_model=self._name, active_ids=self.ids)
        wkf_instance_obj = self.env['workflow.instance']
        user = self.env.user
        if self.get_withdraw_method_flag():
            if not self.env.context.get('has_wizard'):
                return self.with_context(active_model=self._name, active_id=self.id).action_withdraw_wizard()
            if self.task_ids:
                if self.env.context.get('approval_state', False):
                    # 合同上状态字段为approval_state，因原来state字段已被占用
                    wkf_instance = wkf_instance_obj.search([('approve_document', '=', self._name + ',' + str(self.id)),
                                                            ('state', '=', 'processing')])
                    if self.approval_state != 'submitted' and not 'withdraw' in wkf_instance.instance_line_ids.filtered(
                        lambda x: x.state == 'processing').workflow_line_id.signal_type_ids.mapped('code'):
                        raise ValidationError(_("The order is approved or approving,you can't reset it to draft!"))
                else:
                    wkf_instance = wkf_instance_obj.search([('approve_document', '=', self._name + ',' + str(self.id)),
                                                            ('state', '=', 'processing')])
                    if self.state != 'submitted' and not 'withdraw' in wkf_instance.instance_line_ids.filtered(
                        lambda x: x.state == 'processing').workflow_line_id.signal_type_ids.mapped('code'):
                        raise ValidationError(_("The order is approved or approving,you can't reset it to draft!"))
                self.get_wkf_instance(wkf_instance)
            if self.env.context.get('approval_state', False):
                self.approval_state = self._state_selection['created']
            else:
                self.state = self._state_selection['created']

            self.withdraw_time = fields.Datetime.now()
            # 工作流置为撤回状态
            task = self.get_approving_task()
            task.sudo()._wkf_send_email('withdrawn')

            # oa 更新已通知的所有消息的 审批状态 为 已收回
                    # 更新 oa 消息状态
            if self.has_install_module('kthrp_dingding'):
                ding_config_obj = self.env['kthrp.dingding.config']
                # 直发直接更新 oa 消息状态
                if ding_config_obj.search([], limit=1).send_type == 'hrp_direct_send':
                    # 更新钉钉 oa 消息状态
                    if task.dingding_msg_oa:
                        Dingtalk = dingding_interface.KthrpDingtalk
                        oa_task_id, ding_config_id = task.dingding_msg_oa.split('-')[0], task.dingding_msg_oa.split('-')[1]
                        ding_config_obj = self.env['kthrp.dingding.config']
                        corpid, corpsecret, agent_id, token_dict = ding_config_obj.browse(
                            int(ding_config_id)).get_ding_common_message()
                        ding_obj = Dingtalk(corpid, corpsecret, agent_id, token_dict)
                        ding_obj.update_oa_msg_state(int(oa_task_id), '已收回')

                        # 同一节点多个人审批，一人通过，则将其他消息状态同步更新
                        if len(task.wkf_line_instance_id.task_ids) > 1:
                            if task.wkf_line_instance_state == 'processing':
                                for tk in task.wkf_line_instance_id.task_ids:
                                    if tk.dingding_msg_oa:
                                        oa_task_id, ding_config_id = tk.dingding_msg_oa.split('-')[0], tk.dingding_msg_oa.split('-')[1]
                                        ding_config_obj = self.env['kthrp.dingding.config']
                                        corpid, corpsecret, agent_id, token_dict = ding_config_obj.browse(
                                            int(ding_config_id)).get_ding_common_message()
                                        ding_obj = Dingtalk(corpid, corpsecret, agent_id, token_dict)
                                        ding_obj.update_oa_msg_state(int(oa_task_id), '已收回')
                # 前置机发送需要找到对应消息的 后台消息发送列表中的 update_oa_flag 值
                else:
                    update_task_ids = [task.id, -1]
                    # 同一节点多个人审批，一人通过，则将其他消息状态同步更新
                    if len(task.wkf_line_instance_id.task_ids) > 1:
                        if task.wkf_line_instance_state == 'processing':
                            update_task_ids += task.wkf_line_instance_id.task_ids.ids
                    update_sql = "update kthrp_dingding_msg_to_be_sent set update_oa_flag = 't', oa_status_value = '已收回' where workflow_task_id in {};".format(tuple(set(update_task_ids)))
                    self._cr.execute(update_sql)


            self.env['workflow'].set_wkf_back(self._name, self.id)
        else:
            wkf_instance = wkf_instance_obj.search([('approve_document', '=', self._name + ',' + str(self.id)),
                                                    ('state', '=', 'processing')], limit=1)
            current_node = wkf_instance.instance_line_ids.filtered(
                lambda x: x.state == 'processing').workflow_line_id
            current_node_instance = wkf_instance.instance_line_ids.filtered(
                lambda x: x.state == 'processing')
            if current_node_instance and not current_node.countersign and 'withdraw' in current_node.signal_type_ids.mapped(
                    'code'):
                last_current_node_instance = self.env[
                    'workflow.instance.line.transition'].suspend_security().search(
                    [('to_instance_line_id', '=', current_node_instance.id)], limit=1).from_instance_line_id
                last_task = last_current_node_instance.task_ids.filtered(
                    lambda x: x.approve_result_id.code == 'approve' and x.actual_user_id.id == user.id)
                if len(last_task) > 1:
                    # 取最后一个
                    last_task = last_task[-1:]
                if last_current_node_instance and not last_current_node_instance.workflow_line_id.countersign and last_task:
                    self._task_withdraw(current_node_instance, current_node_instance.task_ids[0])
                    new_task = last_task.copy({'approve_result_id': False, 'actual_approver_id': False,
                                               'approve_time': False, 'approve_note': False})
                    last_current_node_instance.write({'state': 'processing', 'process_time': False})
                else:
                    raise ValidationError(_('The current document approval status has been updated, please refresh.'))
            else:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))

    @api.multi
    def get_approving_task(self):
        self.ensure_one()
        if self.task_ids:
            model = self.env['ir.model'].search([('model', '=', self._name)], limit=1)
            task = self.task_ids.search(
                [('approve_result_id', '=', False),
                 ('wkf_model_id', '=', model.id),
                 ('model_instance_id', '=', self.id),
                 ('wkf_line_instance_state', '=', 'processing')], limit=1)
            if task:
                return task

    @api.model
    def _task_withdraw(self, node, current_task):
        node.write({'state': 'withdrawn', 'process_time':fields.Datetime.now()})
        signal_obj = self.env['workflow.signal']
        return_signal = signal_obj.search([('code', '=', 'previous_withdraw')], limit=1)
        for task in node.task_ids.filtered(lambda x: x.id != current_task.id and not x.approve_result_id):
            task.unlink()
        current_task.write({'approve_result_id': return_signal.id, 'actual_approver_id': self.env.user.related_employee_id.id,
                            'approve_note': _('Withdrawn by the previous approver.'), 'approve_time': fields.Datetime.now(),
                            'wkf_line_instance_name': '', 'allocated_approver_id': ''})
        return

    @api.multi
    def set_state_approving(self):
        if self._state_selection['approving']:
            for rec in self:
                self._cr.execute('update %s set state = %%s WHERE state!= %%s AND id=%%s' % rec._table, (self._state_selection['approving'], 'approved', rec.id,))
                # if rec.state != 'approved':
                #     rec.state = self._state_selection['approving']

    @api.multi
    def set_state_rejected(self):
        for rec in self:
            if rec._name == 'kthrp.hr.employment.contract':
                rec.approval_state = self._state_selection['rejected']
            else:
                rec.state = self._state_selection['rejected']

    @job(default_channel='root.base')
    @api.model
    def job_do_after_approved(self, record, lang='en_US'):
        record.suspend_security().with_context(lang=lang).action_after_approved()

    @api.model
    def complete_approve(self, id):
        rec = self.browse(id).suspend_security()
        rec.with_context(track_fields=['state']).write({'state': self._state_selection['approved']})
        lang = self.env.context.get('lang') or self.env.user.lang or 'en_US'
        # self.env.cr.commit()
        self.with_delay(eta=10,max_retries=10).job_do_after_approved(rec, lang=lang)

    @api.multi
    def action_after_approved(self):
        pass

    @api.model
    def refuse_approve(self, id):
        rec = self.browse(id)
        if rec._name == 'kthrp.hr.employment.contract':
            rec.approval_state = self._state_selection['rejected']
        else:
            rec.state = self._state_selection['rejected']

    @api.multi
    def unlink(self):
        """
        单据删除的同时删除单据的审批流实例
        :return:
        """
        wkf_instance_obj = self.env['workflow.instance']
        wkf_instances = wkf_instance_obj
        saved_document_obj = self.env['workflow.saved.document']
        for rec in self:
            document = rec._name + ',' + str(rec.id)
            saved_document = saved_document_obj.search([('document', '=', document)])
            if saved_document:
                saved_document.unlink()
            wkf_instances += wkf_instance_obj.search([('approve_document', '=', self._name + ',' + str(rec.id))])
        for instance in wkf_instances:
            if instance.state != 'withdrawn':
                if not self.env.context.get('background_unlink_summary_payslips', False):
                    raise ValidationError(_("You can't delete the document with approval records."))
        wkf_instances.unlink()
        return super(WorkflowInterface, self).unlink()

    @api.multi
    @api.returns('self', lambda value: value.id)
    def message_post(self, **kwargs):
        self.ensure_one()
        if kwargs.get('workflow_email', False):
            kwargs.pop('workflow_email')
            message = super(WorkflowInterface, self.with_context(mail_create_nosubscribe=True)).message_post(
                **kwargs)
        else:
            message = super(WorkflowInterface, self).message_post(**kwargs)
        if kwargs.get('send_actual_email', False):
            message.send_actual_email = True
        return message


class WorkflowModel(models.Model):
    _name = "workflow.model"
    _description = "Kthrp Base Workflow Model"
    _inherit = "mail.thread"

    name = fields.Char(string="Name", required=True, translate=True)
    model_id = fields.Many2one(comodel_name='ir.model', string='Model', required=True, index=True)
    model_name = fields.Char(related="model_id.model", readonly=1, store=True)
    created_show_cancel = fields.Boolean(string='Shown Cancel when Created', default=False)
    approved_show_cancel = fields.Boolean(string='Shown Cancel when Approved', default=False,
                                          track_visibility="onchange")
    note = fields.Char(string="Note")
    submitted_by_creator = fields.Boolean(string='Submitted by Creator', default=True)
    other_key_ids = fields.One2many(comodel_name='workflow.model.other.key',
                                    string='Other Key Matter', inverse_name='wkf_model_id')
    # 启用传阅
    enable_circulate = fields.Boolean(string='Enable Circulate',
                                      help='After checked, documents can be circulated in the approval process, that is, documents can be sent to corresponding users in the form of messages.')

    action_id = fields.Many2one(comodel_name='ir.actions.act_window')
    values_id = fields.Many2one(comodel_name='ir.values')

    @api.multi
    def write(self, vals):
        # 防止其修改公司数据
        if 'enable_circulate' in vals:
            if vals.get('enable_circulate'):
                for rec in self:
                    if not rec.action_id:
                        action_vals = {'name': _('Circulate'),
                                       'res_model': 'workflow.circulate',
                                       'src_model': rec.model_name,
                                       'target': 'new',
                                       'view_mode': 'form',
                                       'key2': 'client_action_multi',
                                       'multi': False
                                       }
                        action = self.env['ir.actions.act_window'].create(action_vals)
                        ir_values = self.env['ir.values'].create({
                            'name': _('Circulate'),
                            'model_id': rec.model_id.id,
                            'model': rec.model_name,
                            'key': 'action',
                            'key2': 'client_action_multi',
                            'value': "ir.actions.act_window,%s" % str(action.id),
                        })
                        rec.action_id = action.id
                        rec.values_id = ir_values.id
            else:
                for rec in self:
                    if rec.action_id:
                        rec.action_id.unlink()
                    if rec.values_id:
                        rec.values_id.unlink()
        return super(WorkflowModel, self).write(vals)

    @api.model
    def create(self, vals):
        res = super(WorkflowModel, self).create(vals)
        if res.enable_circulate:
            action_vals = {'name': _('Circulate'),
                           'res_model': 'workflow.circulate',
                           'src_model': res.model_name,
                           'target': 'new',
                           'view_mode': 'form',
                           'key2': 'client_action_multi',
                           'multi': False
                           }
            action = self.env['ir.actions.act_window'].create(action_vals)
            ir_values = self.env['ir.values'].create({
                'name': _('Circulate'),
                'model_id': res.model_id.id,
                'model': res.model_name,
                'key': 'action',
                'key2': 'client_action_multi',
                'value': "ir.actions.act_window,%s" % str(action.id),
            })
            res.action_id = action.id
            res.values_id = ir_values.id
        return res


    @api.constrains('model_id')
    def _check_unique_model(self):
        for rec in self:
            if self.search_count([('model_id', '=', rec.model_id.id)]) > 1:
                raise ValidationError(_('The workflow model must be unique！'))

    @api.model
    def init_workflow_by_model(self):
        self = self.sudo()
        workflow_obj = self.env['workflow']
        workflow_line_obj = self.env['workflow.line']
        workflow_line_transition_obj = self.env['workflow.line.transition']
        translation_obj = self.env['ir.translation']
        records = self.search([])
        for rec in records:
            workflow = workflow_obj.with_context(active_test=False).search([('wkf_model_id', '=', rec.model_id.id),
                                                                            ('is_init', '=', True)], limit=1)
            if workflow:
                start_line = workflow.workflow_line_ids.filtered(lambda r: r.node_type == 'start')
                end_line = workflow.workflow_line_ids.filtered(lambda r: r.node_type == 'end')
            else:
                workflow = workflow_obj.create({'name': rec.name,
                                                'wkf_model_id': rec.model_id.id,
                                                'sequence': 10,
                                                'is_init': True,
                                                'withdraw': True})
                start_line = workflow_line_obj.create({'name': 'Workflow Start',
                                                       'workflow_id': workflow.id,
                                                       'sequence': 10,
                                                       'approver_type': False,
                                                       'node_type': 'start'})
                end_line = workflow_line_obj.create({'name': 'Workflow End',
                                                     'workflow_id': workflow.id,
                                                     'sequence': 100,
                                                     'approver_type': False,
                                                     'node_type': 'end'})
                workflow_line_transition_obj.create({'from_workflow_line_id': start_line.id,
                                                     'to_workflow_line_id': end_line.id
                                                     })
            workflow_translation = translation_obj.search([('name', '=', 'workflow.model,name'),
                                                           ('res_id', '=', rec.id),
                                                           ('lang', '=', 'zh_CN')], limit=1)
            if workflow_translation:
                if workflow.with_context({'lang': 'en_US'}).name != workflow_translation.src or workflow.with_context(
                        {'lang': 'zh_CN'}).name != workflow_translation.value:
                    workflow.with_context({'lang': 'en_US', 'permission': True}).write(
                        {'name': workflow_translation.src})
                    workflow.with_context({'lang': 'zh_CN', 'permission': True}).write(
                        {'name': workflow_translation.value})
            if start_line.with_context({'lang': 'zh_CN'}).name != u'开始':
                start_line.with_context({'lang': 'zh_CN', 'permission': True}).write({'name': u'开始'})
            if end_line.with_context({'lang': 'zh_CN'}).name != u'结束':
                end_line.with_context({'lang': 'zh_CN', 'permission': True}).write({'name': u'结束'})


class WorkflowModelOtherKey(models.Model):
    _name = "workflow.model.other.key"

    sequence = fields.Integer(string="Sequence")
    field_id = fields.Many2one('ir.model.fields', string='Field Name', required=True)
    ttype = fields.Selection(related='field_id.ttype', readonly=True, string='Field Type')
    field_description = fields.Char(related='field_id.field_description', readonly=True, string='Field Description')
    wkf_model_id = fields.Many2one(comodel_name='workflow.model', string="Model", ondelete="cascade")
    # model_id = fields.Many2one(related="wkf_model_id.model_id", readonly=True)


class WorkflowRole(models.Model):
    _name = "workflow.role"
    _description = "Kthrp Base Workflow Role"
    _order = 'create_date desc'

    name = fields.Char(string=" Role Name", required=True, translate=True)
    employee_ids = fields.Many2many(comodel_name="hr.employee",
                                    relation='kthrp_wkf_role_employees',
                                    column1='role_id',
                                    column2='employee_id',
                                    string="Employee")
    # 值类型
    role_type = fields.Selection(
        selection=[('static', _('Static')), ('dynamic', _('Dynamic')), ('python', _('Python'))], default="static",
        string="Role Type",
        required=True)
    # 代码
    code = fields.Text(string="Code")
    limited_by_company = fields.Boolean(string='Limited By Company', default=False,
                                        help='If checked, the approvers will be limited by company which the submitter belongs to.')
    line_ids = fields.One2many(comodel_name='workflow.role.line', inverse_name='role_id', string='Role Line')

    active = fields.Boolean(string='Active', default=True)

    auto_approve_when_no = fields.Boolean(string='Auto Approve When No Approver', default=False)

    transfer_id = fields.Many2one(comodel_name='hr.employee', string='Transfer Out If Cannot Flow Out',
                                  help='When the approval role is approved, if the next node cannot find the approver, the workflow will be transferred to the employee for processing.')

    wkf_line_ids = fields.One2many(comodel_name='workflow.line', inverse_name='approve_role_id',
                                   string='Workflow Line', readonly=True)

    wkf_line_transition_ids = fields.Many2many(comodel_name='workflow.line.transition',
                                               string='Workflow line transition',
                                               relation='wkf_transition_wkf_role_rel',
                                               compute='_compute_wkf_line_transition_ids')

    @api.multi
    def _compute_wkf_line_transition_ids(self):
        for rec in self:
            sql = '''SELECT
                        id
                    FROM
                        workflow_line_transition
                    WHERE
                        notify_type = 'approve_role'
                    AND split_part(NOTIFY, ',', 1) = 'workflow.role'
                    AND CAST (
                        split_part(NOTIFY, ',', 2) AS INT
                    ) = %s'''
            self.env.cr.execute(sql, (rec.id,))
            results = self.env.cr.dictfetchall()
            t_ids = [x.get('id') for x in results] or False
            rec.wkf_line_transition_ids = t_ids

    @api.multi
    def toggle_active(self):
        for rec in self:
            if rec.active:
                if self.env['workflow.line'].search_count([('approve_role_id', '=', rec.id)]):
                    raise ValidationError(_('You cannot invalidate an approval role that is in use.'))
            rec.active = not rec.active

    @api.multi
    def write(self, vals):
        res = super(WorkflowRole, self).write(vals)
        for rec in self:
            for wkf_line in rec.wkf_line_ids:
                graph_location_obj = self.env['workflow.graph.location'].search([('workflow_id','=',wkf_line.workflow_id.id)], limit=1)
                if graph_location_obj:
                    location_dict = eval(graph_location_obj.location)
                    node_data_array = location_dict.get('nodeDataArray')
                    for node_data in node_data_array:
                        if node_data.get('id', -1) == str(wkf_line.id):
                            node_data['key'] = wkf_line.name + '\n' + wkf_line.get_all_approved(approve_document=self.env[wkf_line.wkf_model_id.model])
                    graph_location_obj.location = str(location_dict)
                else:
                    pass
        return res

    @api.multi
    def copy(self, default=None):
        default = dict(default or [])
        new_name = _(u"{}(Copy)").format(self.name)
        default['name'] = new_name
        new_line_ids = []
        for line in self.line_ids:
            new_line_ids += line.copy().ids
        default['line_ids'] = [[6, 0, new_line_ids]]
        return super(WorkflowRole, self).copy(default)

    @api.multi
    @api.onchange('limited_by_company')
    def _onchange_limited_by_company(self):
        for rec in self:
            if not rec.limited_by_company:
                rec.line_ids = False
            else:
                rec.employee_ids = False

    @api.multi
    @api.onchange('role_type')
    def _onchange_role_type(self):
        for rec in self:
            rec.code = False

    @api.multi
    @api.constrains('code')
    def _check_code(self):
        wkf_node_obj = self.env['workflow.line']
        for rec in self:
            if rec.code and rec.role_type == "dynamic":
                for node in wkf_node_obj.search([('approve_role_id', '=', rec.id)]):
                    model_name = ''
                    hr_employee_instance = None
                    try:
                        model_name = node.workflow_id and node.workflow_id.wkf_model_id.model or False
                        if model_name:
                            hr_employee_instance = self.env[model_name].mapped(rec.code)
                    except:
                        raise ValidationError(
                            _('The code value is illegal for the model[%s]') % model_name)
                    # 验证取到的值是不是员工
                    if hr_employee_instance != self.env['hr.employee']:
                        raise ValidationError(_('The code value is not employee'))

    def get_role_approver_no_instance(self, approve_document, wkf_node):
        try:
            self = self.suspend_security()
            wkf_role = wkf_node.approve_role_id
            if wkf_role.role_type == 'static':
                if wkf_role.limited_by_company:
                    employee_ids = self.env['hr.employee']
                    if hasattr(approve_document, 'company_id'):
                        company_id = approve_document.company_id.id
                    elif hasattr(approve_document, 'legal_company_id'):
                        company_id = approve_document.legal_company_id.id
                    elif hasattr(approve_document, 'x_company_id'):
                        company_id = approve_document.x_company_id.id
                    else:
                        company_id = False
                    for rec in wkf_role.line_ids:
                        if not company_id or rec.company_id.id == company_id:
                            employee_ids += rec.employee_id
                else:
                    employee_ids = wkf_role.employee_ids
                return employee_ids
            elif wkf_role.role_type == 'dynamic':
                return approve_document.mapped(wkf_role.code)
            elif wkf_role.role_type == 'python':
                eval_context = self._get_eval_context_no_instance(approve_document, wkf_node)
                safe_eval(wkf_role.code.strip(), eval_context, mode="exec", nocopy=True)
                if 'approver' in eval_context:
                    return eval_context['approver']
                else:
                    return []
            else:
                return []
        except Exception as e:
            # raise e
            return []

    @api.model
    def get_role_approver(self, wkf_instance, wkf_node):
        self = self.suspend_security()
        wkf_role = wkf_node.approve_role_id
        if wkf_role.role_type == 'static':
            if wkf_role.limited_by_company:
                employee_ids = self.env['hr.employee']
                if hasattr(wkf_instance.approve_document, 'company_id'):
                    company_id = wkf_instance.approve_document.company_id.id
                elif hasattr(wkf_instance.approve_document, 'legal_company_id'):
                    company_id = wkf_instance.approve_document.legal_company_id.id
                elif hasattr(wkf_instance.approve_document, 'x_company_id'):
                    company_id = wkf_instance.approve_document.x_company_id.id
                elif hasattr(wkf_instance.approve_document, 'x_company'):
                    company_id = wkf_instance.approve_document.x_company.id
                else:
                    company_id = False
                for rec in wkf_role.line_ids:
                    if not company_id or rec.company_id.id == company_id:
                        employee_ids += rec.employee_id
            else:
                employee_ids = wkf_role.employee_ids
            return employee_ids
        elif wkf_role.role_type == 'dynamic':
            return wkf_instance.approve_document.mapped(wkf_role.code)
        elif wkf_role.role_type == 'python':
            eval_context = self._get_eval_context(wkf_instance, wkf_node)
            safe_eval(wkf_role.code.strip(), eval_context, mode="exec", nocopy=True)
            if 'approver' in eval_context:
                return eval_context['approver']
            else:
                return []
        else:
            return []

    @api.model
    def get_role_approver_test(self, doc, wkf_node):
        self = self.suspend_security()
        wkf_role = wkf_node.approve_role_id
        if wkf_role.role_type == 'static':
            if wkf_role.limited_by_company:
                employee_ids = self.env['hr.employee']
                if hasattr(doc, 'company_id'):
                    company_id = doc.company_id.id
                elif hasattr(doc, 'legal_company_id'):
                    company_id = doc.legal_company_id.id
                elif hasattr(doc, 'x_company_id'):
                    company_id = doc.x_company_id.id
                elif hasattr(doc, 'x_company'):
                    company_id = doc.x_company.id
                else:
                    company_id = False
                for rec in wkf_role.line_ids:
                    if not company_id or rec.company_id.id == company_id:
                        employee_ids += rec.employee_id
            else:
                employee_ids = wkf_role.employee_ids
            return employee_ids
        elif wkf_role.role_type == 'dynamic':
            return doc.mapped(wkf_role.code)
        elif wkf_role.role_type == 'python':
            eval_context = self._get_eval_context_no_instance(doc, wkf_node)
            safe_eval(wkf_role.code.strip(), eval_context, mode="exec", nocopy=True)
            if 'approver' in eval_context:
                return eval_context['approver']
            else:
                return []
        else:
            return []

    # 与节点无关的获取审批人
    @api.model
    def get_role_approver_direct(self, wkf_instance, approve_role_id):
        self = self.suspend_security()
        wkf_role = approve_role_id
        if wkf_role.role_type == 'static':
            if wkf_role.limited_by_company:
                employee_ids = self.env['hr.employee']
                if hasattr(wkf_instance.approve_document, 'company_id'):
                    company_id = wkf_instance.approve_document.company_id.id
                elif hasattr(wkf_instance.approve_document, 'legal_company_id'):
                    company_id = wkf_instance.approve_document.legal_company_id.id
                else:
                    company_id = False
                for rec in wkf_role.line_ids:
                    if not company_id or rec.company_id.id == company_id:
                        employee_ids += rec.employee_id
            else:
                employee_ids = wkf_role.employee_ids
            return employee_ids
        elif wkf_role.role_type == 'dynamic':
            return wkf_instance.approve_document.mapped(wkf_role.code)
        elif wkf_role.role_type == 'python':
            eval_context = self._get_eval_context_direct(wkf_instance, approve_role_id)
            safe_eval(wkf_role.code.strip(), eval_context, mode="exec", nocopy=True)
            if 'approver' in eval_context:
                return eval_context['approver']
            else:
                raise ValidationError(
                    _('There isn’t approver in the next approval node, please contact the sysadmin.') + "error: source: %s, code: %s" % (str(wkf_role), wkf_role.code))
        else:
            raise ValidationError(
                _('There isn’t approver in the next approval node, please contact the sysadmin.') + "error: source: %s" % str(wkf_role) )

    @api.model
    def _get_eval_context(self, wkf_instance, wkf_node):
        approve_document = wkf_instance.approve_document
        record = wkf_node.approve_role_id
        return {
            'self': record,
            'approve_document': approve_document,
            'wkf_instance': wkf_instance,
            'wkf_node': wkf_node,
            'time': time,
            'datetime': datetime,
            'dateutil': dateutil,
            'workflow': self.env['workflow'],
            'warning': RedirectWarning,
        }

    @api.model
    def _get_eval_context_no_instance(self, approve_document, wkf_node):
        record = wkf_node.approve_role_id
        return {
            'self': record,
            'approve_document': approve_document,
            'wkf_instance': self.env['workflow.instance'],
            'wkf_node': wkf_node,
            'time': time,
            'datetime': datetime,
            'dateutil': dateutil,
            'workflow': self.env['workflow'],
            'warning': RedirectWarning,
        }

    @api.model
    def _get_eval_context_direct(self, wkf_instance, approve_role_id):
        approve_document = wkf_instance.approve_document
        record = approve_role_id
        return {
            'self': record,
            'approve_document': approve_document,
            'wkf_instance': wkf_instance,
            'time': time,
            'datetime': datetime,
            'dateutil': dateutil,
            'workflow': self.env['workflow'],
            'warning': RedirectWarning,
        }


class WorkflowRoleLine(models.Model):
    _name = "workflow.role.line"
    _description = "Kthrp Base Workflow Role Line"

    role_id = fields.Many2one(comodel_name='workflow.role', string='Role',
                              ondelete='cascade', copy=False, index=True)
    employee_id = fields.Many2one(comodel_name='hr.employee', string='Employee', required=True)
    company_id = fields.Many2one(comodel_name='res.company', string='Company', required=True)
    work_phone = fields.Char(related='employee_id.work_phone', readonly=True, related_sudo=True, string='Work Phone')
    work_email = fields.Char(related='employee_id.work_email', readonly=True, related_sudo=True, string='Work Email')

    @api.multi
    @api.onchange('employee_id')
    def _onchange_employee_id(self):
        for rec in self:
            rec.company_id = rec.employee_id.company_id.id


class WorkflowCondition(models.Model):
    _name = "workflow.condition"
    _description = "Kthrp Base Workflow Condition"
    _order = 'wkf_model_id, create_date desc'

    name = fields.Char(string="Condition Name", required=True, translate=True)
    wkf_model_id = fields.Many2one(comodel_name='ir.model', string="Model", help="Model",
                                   index=True)
    condition_combination = fields.Char(string="Condition Combination",
                                        help="It's for recording the AND/OR relationship of the line conditons. It represents the AND relationship of all the line conditions if it's null.like 1and2.")
    active = fields.Boolean(string="Active", default=True)
    condition_line_ids = fields.One2many(comodel_name='workflow.condition.line', inverse_name='condition_id',
                                         string='Condition Line')

    @api.constrains('wkf_model_id','condition_line_ids')
    def _check_value(self):
        if not self.wkf_model_id:
            if self.condition_line_ids.filtered(lambda x:x.condition_type == 'standard'):
                raise ValidationError(_('Workflow model is required when standard condition lines are included.'))

    @api.multi
    def copy(self, default=None):
        default = dict(default or {})
        new_name = _(u"{}(COPY)").format(self.name)
        default['name'] = new_name
        return super(WorkflowCondition, self).copy(default)

    @api.model
    def _default_wkf_models(self):
        wkf_models = []
        for rec in self.env['workflow.model'].search([]):
            wkf_models.append(rec.model_id.id)
        return self.env['ir.model'].search([('id', 'in', wkf_models)]).ids

    wkf_model_ids = fields.Many2many(comodel_name='ir.model', compute='_compute_wkf_models',
                                     default=_default_wkf_models)

    @api.multi
    def _compute_wkf_models(self):
        for rec in self:
            wkf_models = []
            for record in self.env['workflow.model'].search([]):
                wkf_models.append(record.model_id.id)
                rec.wkf_model_ids = self.env['ir.model'].search([('id', 'in', wkf_models)]).ids

    @api.multi
    @api.constrains('condition_combination', 'condition_line_ids')
    def _validate_condition_combination(self):
        for rec in self:
            condition_line_count = len(rec.condition_line_ids)
            if rec.condition_combination:
                if not re.compile('^\d[or|and\d]+').match(rec.condition_combination):
                    raise ValidationError(_('The workflow condition is illegal,please contact the sysadmin.'))
                elif (rec.condition_combination.count('and') + rec.condition_combination.count(
                        'or') + 1) != condition_line_count:
                    raise ValidationError(_('The workflow condition is illegal,please contact the sysadmin.'))

    # 校验是否符合条件
    @api.multi
    def check_match_domain(self, model_name, record_id, extend_context=None):
        self.ensure_one()
        self = self.suspend_security()
        result_dict = {}
        condition_lines = self.condition_line_ids
        if not model_name or not record_id:
            return False
        for rec in condition_lines:
            if rec.condition_type != 'python':
                if rec.detail_condition == '[]' or rec.detail_condition == '':
                    result_dict[rec.sequence] = 'True'
                else:
                    domain = [('id', '=', record_id)] + eval(rec.detail_condition)
                    count = self.env[model_name].sudo()._search(domain, count=True)
                    if count:
                        result_dict[rec.sequence] = 'True'
                    else:
                        result_dict[rec.sequence] = 'False'
            else:
                eval_context = self._get_eval_context(model_name, record_id)
                eval_context.update(extend_context or {})
                try:
                    safe_eval(rec.code.strip(), eval_context, mode="exec", nocopy=True)
                except Exception as e:
                    result_dict[rec.sequence] = 'False'
                if 'result' in eval_context:
                    result_dict[rec.sequence] = str(eval_context['result'])
                eval_context = None
        result_dict = sorted(result_dict.iteritems(), key=lambda d: d[0])
        condition_result = str(self.condition_combination)
        # 条件组合为空默认所有条件AND关系
        if not self.condition_combination:
            final_result = True
            for k, v in result_dict:
                if v == 'False':
                    final_result = False and final_result
        else:
            for k, v in result_dict:
                condition_result = condition_result.replace(str(k), (' ' + str(v) + ' '))
            self._cr.execute('SAVEPOINT check_before')
            try:
                sql = 'SELECT ' + condition_result
                self.env.cr.execute(sql)
                value = self.env.cr.fetchall()
                final_result = value[0][0]
            except Exception as e:
                final_result = False
                self._cr.execute("ROLLBACK TO SAVEPOINT check_before")
        return final_result

    @api.model
    def _get_eval_context(self, model_name, record_id):
        eval_context = {'env': self.env,
                        'model': self.env[model_name],
                        'record': self.env[model_name].browse(record_id),
                        'user': self.env.user,
                        'condition_boolean': condition_boolean,
                        'time': time,
                        'datetime': datetime,
                        'dateutil': dateutil,
                        'timezone': timezone,
                        }
        return eval_context

    @api.multi
    def copy(self, default=None):
        default = dict(default or [])
        new_name = _(u"{}(Copy)").format(self.name)
        default['name'] = new_name
        new_line_ids = []
        for line in self.condition_line_ids:
            new_line_ids += line.copy().ids
        default['condition_line_ids'] = [[6, 0, new_line_ids]]
        return super(WorkflowCondition, self).copy(default)


class WorkflowConditionLine(models.Model):
    _name = "workflow.condition.line"
    _description = "Kthrp Base Workflow Condition Line"

    condition_id = fields.Many2one(comodel_name='workflow.condition', string="Condition",
                                   ondelete='cascade', index=True)
    sequence = fields.Integer(string='Sequence')
    condition_description = fields.Char(string='Condition Description', translate=True, required=True)
    condition_type = fields.Selection(selection=[('standard', _('Standard')),
                                                 ('custom', _('Custom')),
                                                 ('python', 'Python')],
                                      string='Condition Type',
                                      default='standard',
                                      required=True)
    domain = fields.Text(string="Condition")
    condition_combination = fields.Char(string="Condition Combination", readonly=True,
                                        related='condition_id.condition_combination')
    domain_final = fields.Char(string="Final Condition", store=True)
    detail_condition = fields.Text(string="Detail Condition")
    wkf_model_id = fields.Char(string="Model", compute='_compute_wkf_model')
    code = fields.Text(string='Code')

    @api.multi
    @api.onchange('condition_description', 'condition_id')
    def _get_model(self):
        for rec in self:
            if rec.condition_id and not rec.wkf_model_id:
                rec.wkf_model_id = rec.condition_id.wkf_model_id.model

    @api.multi
    @api.onchange('condition_type')
    def _onchange_condition_type(self):
        self.domain = None
        self.domain_final = None
        self.detail_condition = None
        if self.condition_type == 'python':
            self.code = DEFAULT_PYTHON_CODE

    @api.constrains('code')
    def _check_python_code(self):
        for rec in self.filtered('code'):
            msg = test_python_expr(expr=rec.code.strip(), mode="exec")
            if msg:
                raise ValidationError(msg)

    @api.multi
    @api.depends('condition_id')
    def _compute_wkf_model(self):
        for rec in self:
            rec.wkf_model_id = rec.condition_id.wkf_model_id.model

    @api.multi
    @api.constrains('condition_id', 'detail_condition')
    def _validate_domain(self):
        for rec in self:
            if rec.detail_condition and rec.wkf_model_id:
                try:
                    result = self.env[rec.wkf_model_id].search(eval(rec.detail_condition))
                except Exception as e:
                    raise ValidationError(_('The workflow condition is illegal,please contact the sysadmin.'))

    @api.onchange('domain')
    def onchange_domain(self):
        if self.domain:
            domain_list = json.loads(self.domain)
            domain_final = '['
            first_crycle = True
            for rec in domain_list:
                value = rec.get('value')[0]

                for rec1 in value:
                    if isinstance(rec1, str):
                        if 'u' in rec1:
                            rec1.replace('u', '')
                if isinstance(value[2], unicode):
                    if (value[2] >= u'\u0041' and value[2] <= u'\u005a') or (
                            value[2] >= u'\u0061' and value[2] <= u'\u007a') or not self.boolean_number(value[2]):
                        domain_str = "(" + "'" + value[0] + "'," + "'" + value[1] + "'," + "'" + str(
                            value[2]) + "'" + ")"
                    else:
                        domain_str = "(" + "'" + value[0] + "'," + "'" + value[1] + "'," + " " + value[2] + " " + ")"
                elif isinstance(value[2], (int, float)):
                    domain_str = "(" + "'" + value[0] + "'," + "'" + value[1] + "'," + " " + str(value[2]) + " " + ")"
                else:
                    if value[2]:
                        domain_str = "(" + "'" + value[0] + "'," + "'" + value[1] + "'," + "True" + ")"
                    else:
                        domain_str = "(" + "'" + value[0] + "'," + "'" + value[1] + "'," + "False" + ")"
                if first_crycle:
                    domain_final = domain_final + domain_str
                    first_crycle = False
                else:
                    domain_final = domain_final + ',' + domain_str

            domain_final = domain_final + ']'
            self.domain_final = domain_final
            self.detail_condition = domain_final
            if domain_final == "[]":
                self.domain_final = ''
                self.detail_condition = ''

    @api.model
    def boolean_number(self, string):
        result = True
        try:
            int(float(string))
        except Exception as e:
            result = False
        return result

    @api.multi
    @api.depends('domain')
    def _compute_detail_condition(self):
        for rec in self:
            rec.detail_condition = rec.domain


class Workflow(models.Model):
    _name = "workflow"
    _description = "Kthrp Base Workflow"

    template = fields.Boolean(string='Template', default=False, copy=False)
    template_id = fields.Many2one(comodel_name='workflow', string='Template', copy=False)
    allocation_ids = fields.One2many(comodel_name='workflow', inverse_name='template_id',
                                     string='Allocations')
    copy_mapping = fields.Char(copy=False)
    name = fields.Char(string='Workflow Name', required=True, translate=True)
    wkf_model_id = fields.Many2one(comodel_name="ir.model", string="Model", index=True)
    wkf_condition_id = fields.Many2one(comodel_name="workflow.condition", string="Condition",
                                       ondelete='restrict')
    sequence = fields.Integer(string='Sequence', default=1, index=True)
    withdraw = fields.Boolean(string='Withdraw', default=True,
                              help='If checked, the submitter can withdraw the submitted state document.')
    active = fields.Boolean(string="Active", default=True)
    workflow_line_ids = fields.One2many(comodel_name='workflow.line', string='Workflow Line',
                                        inverse_name='workflow_id')
    is_model_readonly = fields.Boolean(default=False)
    is_init = fields.Boolean(default=False, copy=False)
    extend_message_ids = fields.Many2many(comodel_name='workflow.extend.message',
                                          string="External Notification")
    need_update_diagram = fields.Boolean(default=False, prefetch=False)
    workflow_img = fields.Binary(string='Workflow Img')

    auto_inactive = fields.Boolean(string='Auto Inactive', default=False,
                                   help='When checked, the workflow will be automatically invalidated after all instances using the workflow end, and the new workflow instance will not use the workflow.')

    _sql_constraints = [('sequence_unique', 'UNIQUE(sequence,wkf_model_id)',
                         _("The sequence cannot be repeated in same model.")), ]

    @api.multi
    def open_workflow_diagram(self):
        try:
            self.get_diagram_info_kthrp({'workflow_id': self.id}, skip_load=self.need_update_diagram)
            self.need_update_diagram = False
        except Exception as e:
            raise ValidationError(u'请检查工作流节点连通性。')
        if self.sequence:
            name = self.name+'-'+str(self.sequence),
        else:
            name = self.name
        return {
            'type': 'ir.actions.client',
            'tag': 'workflow_diagram_templete',
            'name': '工作流图形',
            'title': name,
            'context': self.with_context(rec_id=self.id).env.context
        }

    @api.model
    def get_diagram_info_kthrp(self, args, skip_load=False):
        # linkDataArray, from 节点id，to 节点id，text 显示值
        workflow_id = args.get('workflow_id')
        current_id = args.get('current_id')
        save_result = self.env['workflow.graph.location'].get_graph_location_by_workflow_id(workflow_id)
        if save_result and not current_id and not skip_load:
            return save_result
        instance_line_obj = self.env['workflow.instance.line']
        ir_view = self.env['ir.ui.view']
        model, node_obj, conn_obj, src_node, des_node, label, scale = u'workflow', u'workflow.line', u'workflow.line.transition', u'from_workflow_line_id', u'to_workflow_line_id', u"['wkf_condition_name']", (140, 180)
        wkf_id = self.env[model].browse(workflow_id)
        document_model = wkf_id.wkf_model_id.model
        approve_document = self.env[document_model].browse(current_id)
        if save_result and current_id:
            save_result = self.with_context(from_save_result=True).extra_result_process(save_result, wkf_id, approve_document)
            return save_result
        wkf_lines = wkf_id.workflow_line_ids
        node_data_dic = {}
        for line in wkf_lines:
            # domain = [('instance_id.approve_document', '=', document_model + ',' + str(id)), ('workflow_line_id', '=', line.id),
            #           ('instance_id.state', '!=', 'withdrawn')]
            sql = '''SELECT id FROM workflow_instance_line WHERE workflow_line_id = %s AND instance_id = (
                      SELECT ID FROM workflow_instance WHERE "state" != 'withdrawn' AND "approve_document" = %s LIMIT 1)
                      '''
            params = [line.id, document_model + ',' + str(current_id)]
            self._cr.execute(sql, tuple(params))
            instance_lines = self._cr.dictfetchall()
            instance_line = instance_line_obj
            for il in instance_lines:
                instance_line = instance_line_obj.browse(il.get('id'))
            node_id = line
            c_name = line.get_all_approved(approve_document=approve_document, instance_line_id=instance_line)
            # if not instance_line:
            #     pass
            if instance_line.state == 'processing':
                node_data_dic.update({str(node_id.id): {'name': c_name, 'color': "#40A0E9",'white_text':True}})
            else:
                node_data_dic.update({str(node_id.id): {'name': c_name, 'color': "lightgray",'white_text':False}})
        graphs = ir_view.graph_get(int(workflow_id), model, node_obj, conn_obj, src_node, des_node, label, scale)
        result2 = {}
        nodes2 = []
        links2 = []
        x_list =[]
        result2['nodeKeyProperty'] = 'id'
        # 节点
        nodes2_all = graphs.get('nodes')
        start_y = ''
        for node in nodes2_all.values():
            x_list.append(node.get('x'))
            if node.get('name') == '开始':
                start_y = node.get('y')
        max_x = max(x_list) if x_list else 400
        for node2_id in nodes2_all:
            node2_id_body = nodes2_all[node2_id]
            if node2_id_body.get('name') == '开始':
                nodes2.append({
                    'id': node2_id,
                    'loc': str(node2_id_body.get('y'))+ ' ' + str(node2_id_body.get('x')*0.5),
                    'color': '#fff',
                    'size_xy': '400 400',
                    'fig': 'Capsule',
                    'key': node2_id_body.get('name'),
                    'icon':'/kthrp_base/static/src/img/workflow_start.png',
                    'margin_left': 20,
                    'white_text': False,
                    'category': 'start',
                })
            elif node2_id_body.get('name') == '结束':
                if not start_y:
                    start_y = str(node2_id_body.get('y'))
                end_x = node2_id_body.get('x')
                nodes2.append({
                    'id': node2_id,
                    'loc': str(start_y)+ ' ' + str(max_x*0.5) if end_x == max_x else str(start_y)+ ' ' + str(max_x*0.5+100),
                    'color': '#fff',
                    'size_xy': '400 400',
                    'fig': 'Capsule',
                    'key': node2_id_body.get('name'),
                    'icon':'/kthrp_base/static/src/img/workflow_end.png',
                    'margin_left': 20,
                    'white_text': False,
                    'category': 'end',
            })
            else:
                c_color = "#fff"
                c_key = node2_id_body.get('name')
                c_node2_id = node_data_dic.get(node2_id) or ''
                white_text = False
                if c_node2_id:
                    c_color = c_node2_id.get('color')
                    c_name = c_node2_id.get('name')
                    white_text = c_node2_id.get('white_text')
                    if c_name:
                        c_key += '\n'
                        c_key += c_name
                nodes2.append({
                    'id': node2_id,
                    'loc': str(node2_id_body.get('y'))+ ' ' + str(node2_id_body.get('x')*0.5),
                    'color': c_color,
                    'size_xy': '400 400',
                    # 'fig': 'Circle',
                    'key': c_key,
                    'icon':'/kthrp_base/static/src/img/workflow_doing.png',
                    'white_text': white_text
                })
            pass
        # 线条
        transitions2_all = graphs.get('transitions')
        for transitions2_id in transitions2_all:
            transitions2_id_body = transitions2_all[transitions2_id]
            links2.append({
                'from': transitions2_id_body[0],
                'to': transitions2_id_body[1],
                'text': graphs.get('label').get(transitions2_id)[1],
            })
        result2['nodeDataArray'] = nodes2
        result2['linkDataArray'] = links2
        if skip_load:
            self.env['workflow.graph.location'].save_workflow_graph_location({'workflow_id':workflow_id, 'location': str(result2)})
        result2 = self.extra_result_process(result2, wkf_id, approve_document)
        return result2

    def extra_result_process(self, result, wkf_id, approve_document):
        return result

    @api.model
    def _default_wkf_models(self):
        wkf_models = []
        for rec in self.env['workflow.model'].search([]):
            wkf_models.append(rec.model_id.id)
        return self.env['ir.model'].search([('id', 'in', wkf_models)]).ids

    wkf_model_ids = fields.Many2many(comodel_name='ir.model', compute='_compute_wkf_models',
                                     default=_default_wkf_models)

    @api.multi
    def unlink(self):
        for rec in self:
            if rec.is_init:
                raise ValidationError(_('Init Data Is Frozen.'))
        return super(Workflow, self).unlink()

    @api.multi
    def copy(self, default=None):
        self.ensure_one()
        default = dict(default or {})
        new_line_ids = []
        line_mapping = []
        copy_mapping = {}
        for line in self.workflow_line_ids:
            new_line = line.copy()
            new_line_ids += new_line.ids
            temp_list = []
            temp_list.append(new_line.id)
            temp_list.append(line.id)
            line_mapping.append(temp_list)
            copy_mapping[str(line.id)] = new_line.id
        if not default.get('sequence'):
            default['sequence'] = max(self.env['workflow'].with_context(active_test=False).search(
                [('wkf_model_id', '=', self.wkf_model_id.id)]).mapped('sequence')) + 1
        default['workflow_line_ids'] = [[6, 0, new_line_ids]]
        res = super(Workflow, self).copy(default)
        self.env['workflow'].with_delay(eta=2).after_copy(res, line_mapping, copy_mapping)
        return res

    @job
    @api.model
    def after_copy(self, workflow, line_mapping, copy_mapping):
        self = self.suspend_security()
        transition_obj = self.env['workflow.line.transition']
        line_obj = self.env['workflow.line']
        if not workflow or not line_mapping:
            return False
        workflow.copy_mapping = str(copy_mapping)
        for line in workflow.workflow_line_ids:
            for rec in line_mapping:
                if rec[0] == line.id:
                    origin_line = line_obj.browse(rec[1])
                    for transition in origin_line.out_transition_ids:
                        transition_vals = {'workflow_id': workflow.id,
                                           'to_workflow_line_id': self.find_you(transition.to_workflow_line_id.id,
                                                                                line_mapping),
                                           'from_workflow_line_id': line.id,
                                           'sequence': transition.sequence,
                                           'wkf_condition_id': transition.wkf_condition_id.id,
                                           }
                        if transition.notify and transition.notify_sel:
                            transition_vals['notify_sel'] = transition.notify_sel
                            transition_vals['notify'] = transition.notify._name + ',' + str(transition.notify.id)
                        transition_obj.create(transition_vals)
                    break
                else:
                    continue

    @api.model
    def find_you(self, x, datas):
        for data in datas:
            if data[1] == x:
                return data[0]
            else:
                continue

    @property
    def in_use(self):
        if self.env['workflow.instance'].search(
                [('workflow_id', '=', self.id), ('state', '=', 'processing')]):
            return True
        else:
            return False

    @api.multi
    @api.onchange('template')
    def _onchange_template(self):
        for rec in self:
            if rec.template:
                rec.wkf_model_id = False

    @api.multi
    @api.onchange('workflow_line_ids', 'template')
    def _compute_is_model_readonly(self):
        for rec in self:
            if rec.workflow_line_ids.filtered(lambda x: x.node_type == 'approve'):
                rec.is_model_readonly = True
            else:
                if rec.template:
                    rec.is_model_readonly = True
                else:
                    rec.is_model_readonly = False

    @api.onchange('is_init')
    def _onchange_init(self):
        if not self.workflow_line_ids:
            items = []
            item = {
                'node_type': 'start',
                'name': _("Workflow Start"),
                'sequence': 10,
                'approver_type': False

            }
            items.append((0, 0, item))
            item = {
                'node_type': 'end',
                'name': _("Workflow End"),
                'sequence': 100,
                'approver_type': False
            }
            items.append((0, 0, item))
            self.workflow_line_ids = items

    @api.model
    def workflow_menu_translate(self):
        menu = self.env.ref('base.menu_workflow_root', raise_if_not_found=False)
        if menu and menu.name != _("Workflows(obsolete)"):
            menu.with_context({'lang': 'zh_CN'}).write({'name': _("Workflows(obsolete)")})
        else:
            pass

    @api.multi
    def _compute_wkf_models(self):
        for rec in self:
            wkf_models = []
            for record in self.env['workflow.model'].search([]):
                wkf_models.append(record.model_id.id)
            rec.wkf_model_ids = wkf_models

    @api.model
    def create(self, vals):
        res = super(Workflow, self).create(vals)
        if (len(res.workflow_line_ids.filtered(lambda x: x.node_type == 'start')) != 1 or len(
                res.workflow_line_ids.filtered(lambda x: x.node_type == 'end')) != 1) and not res.is_init:
            raise ValidationError(_('There must be only one start or end node in the workflow.'))
        elif 'designated_person' in res.workflow_line_ids.filtered(
                lambda x: x.node_type == 'start').out_transition_ids.mapped('to_workflow_line_id').mapped(
            'approver_type'):
            raise ValidationError(_('The start node can’t designate person to approve.'))
        for line in res.workflow_line_ids.filtered(lambda x: x.node_type == 'approve'):
            approve_signal_id = self.env.ref('workflow.workflow_signal_approve', raise_if_not_found=False).id
            if approve_signal_id not in line.signal_type_ids.ids:
                raise ValidationError(_('There must be approve signal in the approve node.'))
        if res.extend_message_ids:
            special_line = res.workflow_line_ids.filtered(
                lambda x: x.node_type in ('start', 'end') and not x.extend_message_ids)
            for line in special_line:
                line.extend_message_ids = res.extend_message_ids.ids
        return res

    @api.multi
    def write(self, vals):
        # 暂时解决删除缓存内未生成记录报错的问题，备忘录，后续研究彻底解决方法
        if vals.get('workflow_line_ids'):
            for line in vals.get('workflow_line_ids'):
                try:
                    out_transition_ids = line[2].get('out_transition_ids')
                    if out_transition_ids:
                        i = 0
                        for out_line in out_transition_ids:
                            if out_line[0] == 2 and out_line[1][:8] == 'one2many':
                                line[2].get('out_transition_ids').pop(i)
                            i += 1
                except Exception as e:
                    pass
            vals['need_update_diagram'] = True
        res = super(Workflow, self).write(vals)
        skip_flag = False
        if vals.keys() == ['auto_inactive'] or vals.keys() == ['active']:
            skip_flag = True
        for rec in self:
            if rec.template_id and self.env.context.get('manual_opt'):
                raise ValidationError(_('The workflow generated by allocation can be modified uniformly in the template.'))
            if (len(rec.workflow_line_ids.filtered(lambda x: x.node_type == 'start')) != 1 or len(
                    rec.workflow_line_ids.filtered(lambda x: x.node_type == 'end')) != 1) and not rec.is_init:
                raise ValidationError(_('There must be only one start or end node in the workflow.'))
            elif 'designated_person' in rec.workflow_line_ids.filtered(
                    lambda x: x.node_type == 'start').out_transition_ids.mapped('to_workflow_line_id').mapped(
                    'approver_type'):
                raise ValidationError(_('The start node can’t designate person to approve.'))
            # elif not skip_flag and self.env['workflow.instance'].search(
            #         [('workflow_id', '=', rec.id), ('state', '=', 'processing')]) and not self.env.context.get(
            #         'permission'):
            #     raise ValidationError(_('You can’t modify the workflow in use.'))
            for line in rec.workflow_line_ids.filtered(lambda x: x.node_type == 'approve'):
                approve_signal_id = self.env.ref('workflow.workflow_signal_approve', raise_if_not_found=False).id
                if approve_signal_id not in line.signal_type_ids.ids:
                    raise ValidationError(_('There must be approve signal in the approve node.'))
            if rec.extend_message_ids:
                special_line = rec.workflow_line_ids.filtered(
                    lambda x: x.node_type in ('start', 'end') and not x.extend_message_ids)
                for line in special_line:
                    line.extend_message_ids = rec.extend_message_ids.ids
            if rec.template and rec.allocation_ids:
                self.env['workflow'].with_delay(eta=2).after_template_update(rec.allocation_ids, vals)
        return res

    @job
    @api.model
    def after_template_update(self, allocations, vals):
        self = self.suspend_security()
        self = self.with_context(manual_opt=False, lang=self.env.user.lang)
        transition_obj = self.env['workflow.line.transition']

        for allocation in allocations:
            if not allocation.copy_mapping:
                continue
            copy_mapping = safe_eval(allocation.copy_mapping)
            update_vals = copy.deepcopy(vals)
            new_lines = []
            new_transition_change_lines = []
            out_transition_change_lines = []
            if 'workflow_line_ids' in update_vals:
                workflow_line_ids_vals = update_vals.pop('workflow_line_ids')
                for workflow_line in workflow_line_ids_vals:
                    if workflow_line[0] in (2, 4):
                        new_line_id = copy_mapping.get(str(workflow_line[1]))
                        workflow_line[1] = new_line_id
                    elif workflow_line[0] == 0:
                        if 'out_transition_ids' in workflow_line[2]:
                            copy_line_vals = copy.deepcopy(workflow_line[2])
                            copy_line_vals.pop('out_transition_ids')
                            new_transition_change_lines.append(copy_line_vals)
                        else:
                            new_lines.append(copy.deepcopy(workflow_line[2]))
                        workflow_line_ids_vals.pop(workflow_line_ids_vals.index(workflow_line))
                    elif workflow_line[0] == 1:
                        if 'out_transition_ids' in workflow_line[2]:
                            out_transition_change_lines.append(workflow_line[1])
                            workflow_line[2].pop('out_transition_ids')
                        new_line_id = copy_mapping.get(str(workflow_line[1]))
                        workflow_line[1] = new_line_id
                for x in workflow_line_ids_vals:
                    if x[0] == 1 and not x[2]:
                        workflow_line_ids_vals.pop(workflow_line_ids_vals.index(x))
                update_vals['workflow_line_ids'] = workflow_line_ids_vals
            allocation.write(update_vals)
            for new_line in new_lines:
                new_line['workflow_id'] = allocation.id
                wkf_new_line = self.env['workflow.line'].create(new_line)
                origin_line = self.env['workflow.line'].search([('name', '=', wkf_new_line.name),
                                                                           ('sequence', '=', wkf_new_line.sequence),
                                                                           ('node_type', '=', wkf_new_line.node_type),
                                                                           ('workflow_id', '=', allocation.template_id.id)], limit=1)
                if origin_line:
                    copy_mapping[str(origin_line.id)] = wkf_new_line.id
            for new_transition_change_line in new_transition_change_lines:
                new_transition_change_line['workflow_id'] = allocation.id
                wkf_new_line = self.env['workflow.line'].create(new_transition_change_line)
                origin_line = self.env['workflow.line'].search([('name', '=', wkf_new_line.name),
                                                                           ('sequence', '=', wkf_new_line.sequence),
                                                                           ('node_type', '=', wkf_new_line.node_type),
                                                                           ('workflow_id', '=', allocation.template_id.id)], limit=1)
                if origin_line:
                    copy_mapping[str(origin_line.id)] = wkf_new_line.id
                    for transition in origin_line.out_transition_ids:
                        transition_vals = {'workflow_id': allocation.id,
                                           'to_workflow_line_id': copy_mapping.get(str(transition.to_workflow_line_id.id)),
                                           'from_workflow_line_id': wkf_new_line.id,
                                           'sequence': transition.sequence,
                                           'wkf_condition_id': transition.wkf_condition_id.id,
                                           }
                        if transition.notify and transition.notify_sel:
                            transition_vals['notify_sel'] = transition.notify_sel
                            transition_vals['notify'] = transition.notify._name + ',' + str(transition.notify.id)
                        transition_obj.create(transition_vals)

            for out_transition_change_line in out_transition_change_lines:
                new_line_id = copy_mapping.get(str(out_transition_change_line))
                origin_line = self.env['workflow.line'].browse(out_transition_change_line)
                wkf_new_line = self.env['workflow.line'].browse(new_line_id)
                appear_out_transitions = []
                for transition in origin_line.out_transition_ids:
                    same_sequence = wkf_new_line.out_transition_ids.filtered(lambda x: x.sequence == transition.sequence)
                    if same_sequence:
                        same_sequence.write({'wkf_condition_id': transition.wkf_condition_id.id,
                                             'to_workflow_line_id': copy_mapping.get(str(transition.to_workflow_line_id.id)),
                                             'notify_sel': transition.notify_sel,
                                             'notify': transition.notify})
                        appear_out_transitions += same_sequence.ids
                    else:
                        transition_vals = {'workflow_id': allocation.id,
                                           'to_workflow_line_id': copy_mapping.get(str(transition.to_workflow_line_id.id)),
                                           'from_workflow_line_id': wkf_new_line.id,
                                           'sequence': transition.sequence,
                                           'wkf_condition_id': transition.wkf_condition_id.id,
                                           }
                        if transition.notify and transition.notify_sel:
                            transition_vals['notify_sel'] = transition.notify_sel
                            transition_vals['notify'] = transition.notify._name + ',' + str(transition.notify.id)
                        res = transition_obj.create(transition_vals)
                        appear_out_transitions.append(res.id)
                not_appear_out_transitions = wkf_new_line.out_transition_ids.filtered(lambda x: x.id not in appear_out_transitions)
                if not_appear_out_transitions:
                    not_appear_out_transitions.unlink()
            allocation.copy_mapping = str(copy_mapping)











    # 判断重启或新建工作流
    @api.model
    def create_or_restart_wkf(self, model_name, record_id):
        self = self.suspend_security()
        # 记录单据的提交人和提交时间
        rec = self.env[model_name].browse(record_id)
        saved_document_obj = self.env['workflow.saved.document']
        if rec:
            wkf_model = self.env['workflow.model'].search([('model_id.model', '=', model_name)], limit=1)
            exclude_model = ['kthrp.sales.return.order', 'kthrp.sales.order', 'kthrp.purchase.order']
            if wkf_model.submitted_by_creator:
                if (rec._name not in exclude_model and
                        rec.create_uid.id not in [self.env.user.id, SUPERUSER_ID] and self.env.user.id != SUPERUSER_ID):
                    raise ValidationError(_('The current document can only be submitted for approval by the creator!'))
            rec.with_context(track_fields=['submit_user_id', 'submit_time']).\
                write({'submit_user_id': self.env.user.id, 'submit_time': fields.Datetime.now()})
        saved_document_obj.inactive_saved_document(model_name, record_id)
        # 提交按钮的active_id 错误修复

        #特殊拒绝回到拒绝前的节点
        if rec.is_wkf_return:
            reject_instance = self.env['workflow.instance'].search(
                [('approve_document', '=', model_name + ',' + str(rec.id)),
                 ('state', '=', 'rejected')], order='id desc',limit=1)
            reject_instance_line =  reject_instance.instance_line_ids.filtered(lambda x:x.state == 'processed' and x.task_ids.filtered(lambda y:y.approve_result_id.code == 'reject'))
            if reject_instance_line:
                reject_instance_line = reject_instance_line[-1]
                new_reject_instance_line = reject_instance_line.copy({
                    'process_time': False,
                    'state': 'processing'
                })
                reject_task = reject_instance_line.task_ids.filtered(lambda y:y.approve_result_id.code == 'reject')
                reject_instance_line.task_ids._compute_doc_amount()
                if reject_task:
                    if self.env.context.get('submit_note'):
                        approve_note = self.env.context.get('submit_note')
                    else:
                        approve_note = False
                    # 拒绝后的审批节点有多个审批角色，则每个均生成task
                    allocated_approver_ids = []
                    for rec in reject_instance_line.task_ids:
                        if rec.from_task_id:
                            continue
                        if rec.allocated_approver_id.id not in allocated_approver_ids:
                            rec.copy({
                                'actual_approver_id': False,
                                'actual_user_id': False,
                                'approve_time': False,
                                'approve_result_id': False,
                                'approve_note': False,
                                'urgency': reject_task[-1].get_task_urgency() or '1',
                                'wkf_line_instance_id': new_reject_instance_line.id,
                                'dingding_msg_oa': False,
                                'reject_display': True,
                            })
                            allocated_approver_ids.append(rec.allocated_approver_id.id)
                reject_instance.state = 'processing'
                new_reject_instance_line.task_ids.filtered(lambda x: not x.approve_result_id)._wkf_send_email('need_approval')
                return
        ctx = self.env.context.copy()
        if model_name in ['kthrp.expense.report', 'kthrp.contract.contract']:
            ctx.update({'active_ids': [record_id], 'active_id': record_id, 'active_model': model_name})
            self.with_context(ctx).create_kthrp_workflow(model_name, record_id)
        else:
            self.create_kthrp_workflow(model_name, record_id)

    @api.model
    def create_kthrp_workflow(self, model_name, model_instance_id):
        self = self.suspend_security()
        workflow_interface_obj = self.env['workflow.interface']
        # 寻找审批流
        wkf_obj = workflow_interface_obj.get_create_workflow_id(model_name, model_instance_id)
        if not wkf_obj:
            raise ValidationError(_('There isn’t active workflow, please contact the sysadmin.'))
        else:
            work_flow = self.browse(wkf_obj)

        # 初始化审批流
        wkf_instance = work_flow.init_workflow(model_instance_id)
        # 启动审批流
        work_flow.start_workflow(model_instance_id)
        tasks = wkf_instance.instance_line_ids.filtered(lambda x:x.state == 'processing').mapped('task_ids')
        setting = self.env.ref('workflow.workflow_default_setting', raise_if_not_found=False)
        if tasks and len(tasks) == 1 and ((tasks[0].allocated_approver_id.id == self.env.user.related_employee_id.id and setting.submitter_as_first_approver == 'auto_approve') or tasks[0].auto_approve):
            approve_wizard = self.env['workflow.approve']
            approve_user = tasks[0].allocated_user_id
            ctx = {'active_model': 'workflow.task', '_is_task': True, 'active_id': tasks[0].id,
                   'lang': self.env.context.get('lang', 'en_US')}
            if not tasks[0].auto_approve:
                comment = _('Submitter No Need Approval.')
                tasks[0].auto_approve = True
            elif tasks[0].auto_approve and not tasks[0].skip_exception:
                comment = _('Repeat the skip with subsequent nodes.')
            else:
                if self.has_install_module('kthrp_szxys_ext'):
                    comment = _('The application has been processed subsequently!')
                else:
                    comment = _('Auto Approve.')
            record = approve_wizard.with_context(ctx).sudo(approve_user.id).create({'comment': comment})
            x_dict = record.sudo(approve_user.id).action_approve()
        return work_flow

    def init_workflow(self, model_instance_id):
        self = self.suspend_security()
        # 判断是否存在未完成的instance
        wkf_instances = self.get_wkf_instance(model_instance_id, ['processing'])
        if wkf_instances:
            raise ValidationError(
                _('Error:Exists processing instance!Please complete them first!'))

        apply_employee_ids = self.env.user.related_employee_id

        if len(apply_employee_ids) == 0:
            raise ValidationError(
                _('Error:Current user has not employee!'))

        # 创建instance
        wkf_instance = self.env['workflow.instance'].create({'workflow_id': self.id,
                                                                        # 'env_params': self._context,
                                                                        'submit_user_id': self.env.user.id,
                                                                        'model_instance_id': model_instance_id,
                                                                        'approve_document': self.wkf_model_id.model + ',' + str(
                                                                            model_instance_id),
                                                                        'apply_employee_id': apply_employee_ids[0].id,
                                                                        'state': 'processing',
                                                                        'document_name':
                                                                            self.env[self.wkf_model_id.model].browse(
                                                                                model_instance_id).name_get()[0][1],
                                                                        })
        wkf_instance.init_exec(apply_employee_ids[0].id)
        return wkf_instance

    def start_workflow(self, model_instance_id):
        self = self.suspend_security()
        wkf_instances = self.get_wkf_instance(model_instance_id, ['processing'])
        if len(wkf_instances) == 0:
            raise ValidationError(
                _('Error:Cannot find workflow instance!'))
        elif len(wkf_instances) > 1:
            raise ValidationError(
                _('Error:Find more than one workflow instance!'))
        else:
            wkf_instances[0].start()

    def get_wkf_instance(self, model_instance_id, wkf_instance_states):
        wkf_instances = None
        if wkf_instance_states:
            wkf_instances = self.env['workflow.instance'].search(
                [('workflow_id', '=', self.id), ('model_instance_id', '=', model_instance_id),
                 ('state', 'in', wkf_instance_states)])
        else:
            wkf_instances = self.env['workflow.instance'].search(
                [('workflow_id', '=', self.id), ('model_instance_id', '=', model_instance_id)])
        return wkf_instances

    @api.model
    def set_wkf_back(self, model_name, record_id):
        self = self.suspend_security()
        # work_flow = self.search([('model.model', '=', model_name), ('is_active', '=', True)])
        wkf_instances = self.env['workflow.instance'].search(
            [('workflow_id.wkf_model_id.model', '=', model_name), ('model_instance_id', '=', record_id),
             ('state', '=', 'processing')])
        saved_document_obj = self.env['workflow.saved.document']
        saved_document_obj.active_saved_document(model_name, record_id)
        if len(wkf_instances) != 1:
            return False
        else:
            work_flow = wkf_instances.workflow_id

        if len(work_flow) != 1:
            return False
        else:
            work_flow.callback_workflow(record_id)

    def callback_workflow(self, model_instance_id):
        self = self.suspend_security()
        self.get_wkf_instance(model_instance_id, ['processing']).callback()

    allocation_count = fields.Integer(compute='_compute_allocation_count')

    @api.multi
    def _compute_allocation_count(self):
        for rec in self:
            rec.allocation_count = len(self.allocation_ids.ids)

    @api.multi
    def view_allocations(self):
        self.ensure_one()
        self = self.suspend_security()
        ids = self.allocation_ids.ids
        domain = [('id', 'in', ids)]
        action = self.env.ref('workflow.action_workflow').read(
                ['name', 'type', 'res_model', 'view_type', 'view_mode', 'target', 'domain', 'context', 'view_id',
                 'search_view_id'])[0]
        action['domain'] = domain
        return action

    @api.multi
    def view_template(self):
        self.ensure_one()
        self = self.suspend_security()
        template_id = self.template_id.id
        action = self.env.ref('workflow.action_workflow').read(
                ['name', 'type', 'res_model', 'view_type', 'view_mode', 'target', 'domain', 'context', 'view_id',
                 'search_view_id'])[0]
        action['view_mode'] = 'form'
        action['res_id'] = template_id
        return action


class WorkflowLine(models.Model):
    _name = "workflow.line"
    _description = "Kthrp Base Workflow Line"
    _order = "sequence"

    workflow_id = fields.Many2one(comodel_name='workflow', string='Workflow', ondelete='cascade',
                                  copy=False, index=True)
    related_line_id = fields.Many2one(comodel_name='workflow.line', string='Related Node',
                                      copy=False)
    wkf_model_id = fields.Many2one(related="workflow_id.wkf_model_id", string="Model", store=True, readonly=True)
    wkf_sequence = fields.Integer(related="workflow_id.sequence", string='Sequence', readonly=True)
    wkf_active = fields.Boolean(related="workflow_id.active", string='Active', readonly=True)
    sequence = fields.Integer(string='Sequence', default=10, required=True)
    name = fields.Char(string='Node Name', required=True, translate=True)
    node_type = fields.Selection(selection=[('start', _('Start')),
                                            ('end', _('End')),
                                            ('approve', _('Approve'))],
                                 string='Node Type',
                                 required=True,
                                 default='approve', index=True)

    def _get_default_signal_type(self):
        approve = self.env['workflow.signal'].search([('code', '=', 'approve'), ('type', '=', 'normal')],
                                                                limit=1)
        reject = self.env['workflow.signal'].search([('code', '=', 'reject'), ('type', '=', 'normal')],
                                                               limit=1)
        return (approve + reject).ids

    signal_type_ids = fields.Many2many(comodel_name='workflow.signal', string='Signal Type',
                                       relation='kthrp_wkf_node_signal',
                                       column1='node_id',
                                       column2='signal_id',
                                       domain=['|', ('type', '=', 'normal'), ('code', '=', 'withdraw')],
                                       default=_get_default_signal_type)
    approver_type = fields.Selection(selection=[('approve_role', _('Approve Role')),
                                                ('related_person', _('Related Person')),
                                                ('designated_person', _('Designated Person')),
                                                ('designated_position', _('Designated Position')),
                                                ('dynamic_selection', _('Dynamic Selection'))],
                                     string='Approver Type',
                                     default='approve_role'
                                     )
    approve_role_id = fields.Many2one(comodel_name='workflow.role', string='Approve Role',
                                      ondelete='restrict')
    approver_source = fields.Selection(selection=[('submitter', _('Submitter')),
                                                  ('last_approver', _('Last Approver')),
                                                  ('model_person', _('Model Person'))],
                                       default='submitter',
                                       string='Approver Source')
    related_person = fields.Selection(selection=[('manager', 'Manager'),
                                                 ('department_head', 'Department Head'),
                                                 ('parent_position', 'Parent Position')], string='Related Person')
    related_field = fields.Many2one(comodel_name='ir.model.fields', string='Model Person')
    countersign = fields.Boolean(string='Countersign', default=False)
    in_transition_ids = fields.One2many(comodel_name='workflow.line.transition',
                                        inverse_name='to_workflow_line_id',
                                        string='To Workflow Transition',
                                        copy=False)
    out_transition_ids = fields.One2many(comodel_name='workflow.line.transition',
                                         inverse_name='from_workflow_line_id',
                                         string='From Workflow Transition',
                                         copy=False)
    context_wkf_id = fields.Integer(default=lambda self: self.env.context.get('_wkf_id'), compute='_compute_wkf_id')
    extend_message_ids = fields.Many2many(comodel_name='workflow.extend.message',
                                          string="External Notification")
    notify_by_end = fields.Boolean(string='Notify By End', default=False,
                                   help='If checked, the node approvers will be notified when the workflow ends.')

    def _get_default_business_group(self):
        return self.env.user.company_id.business_group_id

    business_group_id = fields.Many2one('business.group', string="Business Group",
                                        default=_get_default_business_group)
    repeat_approve = fields.Boolean(string='Repeat Approve', defalut=False,
                                    help='If checked， user can approve repeatedly in one workflow.')
    non_adj_repeat_approve = fields.Boolean(string='Non-adjacent Nodes Repeat Approval', default=True,
                                            help='When this node and the approved node in the workflow are the same approver, if unchecked, approval will be skipped automatically. If checked, approval will be required to be repeated.')
    attachment_required = fields.Boolean(string='Attachment Required', defalut=False,
                                         help='If checked, user need to upload one attachment at least when approve.')
    substitution_rule_ids = fields.One2many(comodel_name='workflow.substitution.rule',
                                            inverse_name='workflow_line_id',
                                            string='Substitution Rules')
    # 判断是否是确认节点
    is_check_node = fields.Boolean(string='Check Node', default=False)

    # 拿到当前节点所有审批人
    @api.multi
    def get_all_approved(self, approve_document, instance_line_id=False):
        self = self.suspend_security()
        wkf_node = self
        all_approved_ids = self.env['hr.employee']
        wkf_role_obj = self.env['workflow.role']
        approved_roles = self.env['hr.employee']
        if instance_line_id:
            for task_id in instance_line_id.task_ids:
                if task_id.actual_approver_id:
                    all_approved_ids += task_id.actual_approver_id
                    if task_id.actual_approver_id not in approved_roles:
                        approved_roles += task_id.actual_approver_id
                else:
                    all_approved_ids += task_id.allocated_approver_id
        else:
            if self.approver_type == 'approve_role':
                for approver in wkf_role_obj.get_role_approver_no_instance(approve_document=approve_document, wkf_node=wkf_node):
                    all_approved_ids += approver
            elif self.approver_type == 'related_person':
                pass
            elif self.approver_type == 'designated_person':
                pass
            elif self.approver_type == 'designated_position':
                position_employee = self.env['hr.employee'].search([('position_id', '=', self.position_id.id)])
                all_approved_ids += position_employee
            else:
                pass
        if all_approved_ids:
            return_all_approved_ids = '(%s)' % '/'.join(all_approved_ids.mapped('name'))
            if approved_roles:
                for approved_role in approved_roles:
                    return_all_approved_ids = return_all_approved_ids.replace(approved_role.name, u'√'+approved_role.name)
        else:
            return_all_approved_ids = ''
        return return_all_approved_ids

    @api.multi
    def check_substitution_rule(self, instance, employee_id):
        self.ensure_one()
        self = self.suspend_security()
        substitution_id = None
        if not self.substitution_rule_ids:
            return employee_id
        for line in self.substitution_rule_ids:
            if line.wkf_condition_id.check_match_domain(instance.approve_document._name, instance.approve_document.id,
                                                        extend_context={'candidate': employee_id}) is True:
                substitution_id = line.get_substitution_approver(instance)
                break
            else:
                continue
        return substitution_id

    @api.multi
    @api.onchange('sequence')
    def _onchange_sequence(self):
        for rec in self:
            if rec.workflow_id.wkf_model_id:
                rec.wkf_model_id = rec.workflow_id.wkf_model_id

    @api.multi
    @api.depends('name', 'workflow_id')
    def _compute_wkf_id(self):
        for rec in self:
            rec.context_wkf_id = self.env.context.get('_wkf_id')
            if not rec.context_wkf_id:
                rec.context_wkf_id = rec.workflow_id.id
            rec.extend_message_ids = rec.workflow_id.extend_message_ids

    @api.constrains('countersign')
    @api.multi
    def _check_countersign(self):
        for rec in self:
            if rec.countersign and rec.approver_type == 'designated_person':
                rec.countersign = False
                raise ValidationError(
                    _('Countersign node can not designate the approver.'))
            if rec.countersign:
                for line in rec.out_transition_ids:
                    if line.to_workflow_line_id.approver_type == 'designated_person':
                        rec.countersign = False
                        raise ValidationError(
                            _('Next node is designated-person-node so you can not countersign.'))

    @api.onchange('approver_type')
    def _approver_type_change(self):
        if self.approver_type == 'related_person':
            self.approve_role_id = None
            self.approver_source = 'submitter'
        elif self.approver_type == 'approve_role':
            self.approver_source = None
            self.related_person = None
            self.related_field = None
        elif self.approver_type == 'designated_person':
            self.approver_source = None
            self.approve_role_id = None
            self.related_field = None
            self.countersign = False
            for rec in self.in_transition_ids:
                if rec.from_workflow_line_id.node_type == 'start' or rec.from_workflow_line_id.countersign:
                    self.approver_type = None

    # 获取最后审批人
    def get_last_approver(self):
        approvers = self.env['workflow.task'].search(
            [('wkf_line_instance_id.workflow_line_id', '=', self.id), ('actual_approver_id', '!=', False)],
            order='approve_time desc')
        if len(approvers) > 0:
            return approvers[0].actual_approver_id
        else:
            return None

    @property
    def is_start(self):
        if self.node_type == 'start':
            return True
        else:
            return False

    @property
    def is_end(self):
        if self.node_type == 'end':
            return True
        else:
            return False

    @api.multi
    @api.onchange('node_type')
    def _onchange_node_type(self):
        if self.node_type == 'approve':
            approve = self.env['workflow.signal'].search([('code', '=', 'approve'), ('type', '=', 'normal')],
                                                                    limit=1)
            reject = self.env['workflow.signal'].search([('code', '=', 'reject'), ('type', '=', 'normal')],
                                                                   limit=1)
            self.signal_type_ids = (approve + reject).ids
        else:
            self.approver_type = False
            self.approve_role_id = False
            self.extend_message_ids = False
            self.signal_type_ids = False
            self.countersign = False
            self.attachment_required = False
            self.repeat_approve = False
            self.non_adj_repeat_approve = False
            self.notify_by_end = False
            self.related_person = False
            self.approver_source = False
            self.related_field = False

    @api.model
    def create(self, vals):
        res = super(WorkflowLine, self).create(vals)
        if res.workflow_id:
            # if res.workflow_id.in_use:
            #     raise ValidationError(_('You can’t modify the workflow in use.'))
            if not res.workflow_id.is_init and res.node_type == 'start' and self.search(
                    [('node_type', '=', 'start'), ('workflow_id', '=', res.workflow_id.id), ('id', '!=', res.id)]):
                raise ValidationError(_('There must be only one start or end node in the workflow.'))
            elif not res.workflow_id.is_init and res.node_type == 'end' and self.search(
                    [('node_type', '=', 'end'), ('workflow_id', '=', res.workflow_id.id), ('id', '!=', res.id)]):
                raise ValidationError(_('There must be only one start or end node in the workflow.'))
            elif 'designated_person' in res.workflow_id.workflow_line_ids.filtered(
                    lambda x: x.node_type == 'start').out_transition_ids.mapped('to_workflow_line_id').mapped(
                'approver_type'):
                raise ValidationError(_('The start node can’t designate person to approve.'))
            for line in res.workflow_id.workflow_line_ids.filtered(lambda x: x.node_type == 'approve'):
                approve_signal_id = self.env.ref('workflow.workflow_signal_approve', raise_if_not_found=False).id
                if approve_signal_id not in line.signal_type_ids.ids:
                    raise ValidationError(_('There must be approve signal in the approve node.'))
        return res

    @api.multi
    def write(self, vals):
        res = super(WorkflowLine, self).write(vals)
        for rec in self:
            if rec.workflow_id:
                # if rec.workflow_id.in_use and not self.env.context.get('permission'):
                #     raise ValidationError(_('You can’t modify the workflow in use.'))
                if not rec.workflow_id.is_init and rec.node_type == 'start' and self.env[
                    'workflow.line'].search(
                        [('node_type', '=', 'start'), ('workflow_id', '=', rec.workflow_id.id), ('id', '!=', rec.id)]):
                    raise ValidationError(_('There must be only one start or end node in the workflow.'))
                elif not rec.workflow_id.is_init and rec.node_type == 'end' and self.env[
                    'workflow.line'].search(
                        [('node_type', '=', 'end'), ('workflow_id', '=', rec.workflow_id.id), ('id', '!=', rec.id)]):
                    raise ValidationError(_('There must be only one start or end node in the workflow.'))
                elif 'designated_person' in rec.workflow_id.workflow_line_ids.filtered(
                        lambda x: x.node_type == 'start').out_transition_ids.mapped('to_workflow_line_id').mapped(
                    'approver_type'):
                    raise ValidationError(_('The start node can’t designate person to approve.'))
                for line in rec.workflow_id.workflow_line_ids.filtered(lambda x: x.node_type == 'approve'):
                    approve_signal_id = self.env.ref('workflow.workflow_signal_approve', raise_if_not_found=False).id
                    if approve_signal_id not in line.signal_type_ids.ids:
                        raise ValidationError(_('There must be approve signal in the approve node.'))
        return res

    @api.multi
    def unlink(self):
        for rec in self:
            if rec.workflow_id:
                if self.env['workflow.instance.line'].search(
                         [('workflow_line_id', '=', rec.id), ('state', '=', 'processing')]):
                    raise ValidationError(_('You can’t modify the workflow in use.'))
                if rec.workflow_id.template:
                    if self.env['workflow.instance.line'].search(
                            [('workflow_line_id', 'in', rec.workflow_id.allocation_ids.mapped('workflow_line_ids').filtered(lambda x:x.sequence == rec.sequence).ids), ('state', '=', 'processing')]):
                        raise ValidationError(_('You can’t modify the workflow in use.'))
                if (len(rec.workflow_id.workflow_line_ids.filtered(lambda x: x.node_type == 'start')) != 1 or len(
                        rec.workflow_id.workflow_line_ids.filtered(
                            lambda x: x.node_type == 'end')) != 1) and not rec.workflow_id.is_init:
                    raise ValidationError(_('There must be only one start or end node in the workflow.'))
        return super(WorkflowLine, self).unlink()

    @api.multi
    def name_get(self):
        result = []
        if self.env.context.get('from_task_id'):
            from_task = self.env['workflow.task'].browse(self.env.context.get('from_task_id'))
            approve_result = from_task.with_context(prefetch_fields=False).approve_result_id
            if approve_result.code in ['transfer', 'linkup', 'add']:
                for rec in self:
                    complete_name = '  '
                    result.append((rec.id, complete_name))
                return result
            else:
                return super(WorkflowLine, self).name_get()
        else:
            return super(WorkflowLine, self).name_get()


class WorkflowLineTransition(models.Model):
    _name = "workflow.line.transition"
    _description = "Kthrp Base Workflow Line Transition"

    workflow_id = fields.Many2one(related='from_workflow_line_id.workflow_id', string='Workflow', readonly=True)
    wkf_sequence = fields.Integer(related="workflow_id.sequence", string='Sequence', readonly=True)
    wkf_active = fields.Boolean(related="workflow_id.active", string='Active', readonly=True)
    to_workflow_line_id = fields.Many2one(comodel_name='workflow.line', string='Target Node',
                                          ondelete='cascade',
                                          index=True,
                                          copy=False,
                                          domain=[('node_type', '!=', 'start')])
    from_workflow_line_id = fields.Many2one(comodel_name='workflow.line', string='Source Node',
                                            ondelete='cascade',
                                            copy=False,
                                            index=True)
    line_sequence = fields.Integer(related='from_workflow_line_id.sequence', readonly=True)
    line_name = fields.Char(related='from_workflow_line_id.name', readonly=True)
    node_type = fields.Selection(related='from_workflow_line_id.node_type', readonly=True, store=True)
    sequence = fields.Integer(string='Sequence', default=10, required=True)
    wkf_condition_id = fields.Many2one(comodel_name='workflow.condition', string='Condition',
                                       ondelete='restrict')
    wkf_condition_name = fields.Char(readonly=True,
                                     compute='_compute_wkf_condition_name')
    notify_type = fields.Selection([('approve_role', 'Approve Role'), ('designated_person', 'Designated Person')],
                                   string='Notify Type')
    notify_sel = fields.Char()
    notify = fields.Reference(selection='_select_objects', string="Notify")
    wkf_model_id = fields.Many2one(comodel_name='ir.model', string="Model", readonly=True,
                                   compute='_compute_wfk_model_id')
    context_wkf_id = fields.Integer(default=lambda self: self.env.context.get('_wkf_id'), compute='_compute_wkf_id')
    name = fields.Char(string="Name", compute='_compute_name')
    red = fields.Boolean(string='Red',default=False)

    @api.multi
    @api.depends('to_workflow_line_id', 'from_workflow_line_id')
    def _compute_name(self):
        for rec in self:
            name_from = rec.from_workflow_line_id.name if rec.from_workflow_line_id else 'N/A'
            name_to = rec.to_workflow_line_id.name if rec.to_workflow_line_id else 'N/A'
            rec.name = name_from + ' To ' + name_to

    @api.model
    def _select_objects(self):
        models = ('workflow.role', 'hr.employee')
        records = self.env['ir.model'].search([('model', 'in', models)])
        return [(record.model, record.name) for record in records] + [('', '')]

    @api.multi
    @api.onchange('notify_type')
    def _onchange_notify_type(self):
        for rec in self:
            if rec.notify_type == 'approve_role':
                rec.notify_sel = 'workflow.role'
            elif rec.notify_type == 'designated_person':
                rec.notify_sel = 'hr.employee'

    @api.multi
    @api.onchange('to_workflow_line_id')
    def _onchange_to_workflow_line_id(self):
        for rec in self:
            if not rec.wkf_model_id:
                rec.wkf_model_id = rec.to_workflow_line_id.workflow_id.wkf_model_id

    @api.multi
    @api.depends('wkf_condition_id', 'sequence')
    def _compute_wkf_condition_name(self):
        for rec in self:
            if rec.wkf_condition_id:
                rec.wkf_condition_name = '%s-%s' % (str(rec.sequence), rec.wkf_condition_id.name)
            else:
                rec.wkf_condition_name = False

    @api.multi
    @api.depends('sequence', 'workflow_id')
    def _compute_wkf_id(self):
        for rec in self:
            rec.context_wkf_id = self.env.context.get('_wkf_id')
            if not rec.context_wkf_id:
                rec.context_wkf_id = rec.workflow_id.id

    @api.multi
    @api.depends('sequence', 'workflow_id')
    def _compute_wfk_model_id(self):
        for rec in self:
            wkf_id = self.env.context.get('_wkf_id')
            if wkf_id:
                rec.wkf_model_id = self.env['workflow'].browse(wkf_id).wkf_model_id.id
            else:
                rec.wkf_model_id = rec.workflow_id.wkf_model_id.id

    @api.model
    def create(self, vals):
        res = super(WorkflowLineTransition, self).create(vals)
        if res.workflow_id:
            if res.workflow_id.in_use:
                pass
                # raise ValidationError(_('You can’t modify the workflow in use.'))
            elif 'designated_person' in res.workflow_id.workflow_line_ids.filtered(
                    lambda x: x.node_type == 'start').out_transition_ids.mapped('to_workflow_line_id').mapped(
                'approver_type'):
                raise ValidationError(_('The start node can’t designate person to approve.'))
        return res

    @api.multi
    def write(self, vals):
        res = super(WorkflowLineTransition, self).write(vals)
        for rec in self:
            if rec.workflow_id:
                if rec.workflow_id.in_use:
                    pass
                    # raise ValidationError(_('You can’t modify the workflow in use.'))
                elif 'designated_person' in rec.workflow_id.workflow_line_ids.filtered(
                        lambda x: x.node_type == 'start').out_transition_ids.mapped('to_workflow_line_id').mapped(
                    'approver_type'):
                    raise ValidationError(_('The start node can’t designate person to approve.'))
        return res

    # @api.multi
    # def unlink(self):
    #     for rec in self:
    #         if rec.workflow_id:
    #             if rec.workflow_id.in_use:
    #                 raise ValidationError(_('You can’t modify the workflow in use.'))
    #     return super(WorkflowLineTransition, self).unlink()


class WorkflowInstance(models.Model):
    _name = "workflow.instance"
    _description = "Kthrp Base Workflow Instance"
    _rec_name = "approve_document"
    _going_live_clear_cls = 'clearable'

    approve_document = fields.Reference(string='Approve Document', readonly=True, selection='_select_objects',
                                        required=True)
    workflow_id = fields.Many2one(comodel_name='workflow', string='Workflow', ondelete='restrict',
                                  required=True, index=True)
    # 审批状态
    instance_approve_state = fields.Selection(related='current_line_instance_id.instance_id.state', readonly=True,
                                              string="Approve State")
    wkf_model_id = fields.Many2one(related='workflow_id.wkf_model_id', string='Model', store=True, readonly=True)
    model_instance_id = fields.Integer(string="Model Instance")
    state = fields.Selection(string="State",
                             selection=[('processing', 'Processing'),
                                        ('completed', 'Completed'),
                                        ('rejected', 'Rejected'),
                                        ('withdrawn', 'Withdrawn')],
                             index=True,
                             required=True,
                             track_visibility='onchange')
    instance_line_ids = fields.One2many(comodel_name='workflow.instance.line',
                                        inverse_name='instance_id',
                                        string='Instance Line')
    apply_employee_id = fields.Many2one(comodel_name='hr.employee', string='Apply Employee')
    submit_user_id = fields.Many2one(comodel_name="res.users", string="Submitter")
    document_name = fields.Char(string='Approve Document', compute='_compute_document_name', store=True, readonly=False)
    submit_time = fields.Datetime(string='Submit Time', compute='_compute_submit_time', store=True, readonly=True)
    document_note = fields.Char(string='Document Note', compute='_compute_note', store=True, readonly=True)
    current_line_instance_id = fields.Many2one(comodel_name="workflow.instance.line",
                                               string="Current Node Name", compute='_compute_current_line_instance')
    company_id = fields.Many2one(comodel_name='res.company', string='Company', compute="_compute_document_company",
                                 store=True, readonly=True)
    # 围绕这个字段,作者将很不情愿的写下一段逻辑来满足数据筛选的  需求
    is_display = fields.Boolean(default=True)
    fuzzy = fields.Char(prefetch=False)

    other_key_matter = fields.Char(string='Other Key Matter', compute='_compute_other_matter')
    doc_state = fields.Char(string='Document State', compute='_compute_doc_state')

    @api.multi
    @api.depends('approve_document')
    def _compute_doc_state(self):
        for rec in self:
            state = False
            try:
                if rec.approve_document and hasattr(rec.approve_document, 'state'):
                    doc_model = self.env[rec.approve_document._name]
                    field_cls = doc_model._fields.get('state')
                    description = field_cls.get_description(doc_model.env)
                    selections = description.get('selection', [])
                    for x in selections:
                        if x[0] == rec.approve_document.state:
                            state = x[1]
                            break
            except Exception as e:
                pass
            rec.doc_state = state

    @api.multi
    @api.depends('approve_document')
    def _compute_other_matter(self):
        for rec in self:
            try:
                result = ''
                if rec.approve_document:
                    approve_document = rec.approve_document.suspend_security()
                    ref_model = self.env['workflow.model'].search([('model_id', '=', rec.workflow_id.wkf_model_id.id)], limit=1)
                    if ref_model.other_key_ids:
                        for x in ref_model.other_key_ids:
                            field_name = x.field_id.name
                            if x.ttype == 'many2one':
                                x_relation_record = getattr(approve_document, field_name).suspend_security()
                                if x_relation_record:
                                    value = x_relation_record.name_get()[0][1]
                                else:
                                    value = ''
                            elif x.ttype == 'selection':
                                doc_model = self.env[approve_document._name]
                                field_cls = doc_model._fields.get(field_name)
                                description = field_cls.get_description(doc_model.env)
                                selections = description.get('selection', [])
                                value = getattr(approve_document, field_name)
                                for y in selections:
                                    if y[0] == value:
                                        value = y[1]
                                        break
                            else:
                                value = getattr(approve_document, field_name)
                            if value:
                                result += '[%s]:%s;' % (x.field_description, str(value))
            except Exception as e:
                result = ''
            if result:
                rec.other_key_matter = result

    @api.model
    def create(self, vals):
        if vals.get('approve_document'):
            other_instances = self.search([('approve_document', '=', vals.get('approve_document'))])
            if other_instances:
                other_instances.write({'is_display': False})
        return super(WorkflowInstance, self).create(vals)

    @api.multi
    @api.depends('approve_document')
    def _compute_document_name(self):
        lang = self.env.user.lang
        for rec in self:
            try:
                if rec.approve_document:
                    approve_document_model = rec.approve_document._name
                    if approve_document_model == 'kthrp.hr.leave.record':
                        document_type = '休假申请' if lang == 'zh_CN' else 'Leave App'
                    elif approve_document_model == 'kthrp.hr.resumption.leave.application':
                        document_type = '销假申请' if lang == 'zh_CN' else 'Resumption Leave App'
                    elif approve_document_model == 'kthrp.hr.outwork.application':
                        document_type = '外出申请' if lang == 'zh_CN' else 'Outwork App'
                    elif approve_document_model == 'kthrp.hr.employee.change.apply':
                        if rec.approve_document.type_id.name:
                            document_type = str(rec.approve_document.type_id.name)
                        else:
                            document_type = ''
                    elif approve_document_model == 'kthrp.hr.employee.change.resign':
                        document_type = '离职申请' if lang == 'zh_CN' else 'Resign App'
                    elif approve_document_model == 'kthrp.hr.employee.change.global.transfer':
                        document_type = '跨公司调动' if lang == 'zh_CN' else 'Global Transfer'
                    elif approve_document_model == 'kthrp.hr.chanyi.employee.entry.application':
                        document_type = '入职申请' if lang == 'zh_CN' else 'Employee Entry Application'
                    else:
                        document_type = ''
                    rec.document_name = (str(
                        rec.approve_document.with_context(for_task=True).name_get()[0][1]) or 'N/A') + document_type
            except Exception as e:
                pass

    @api.multi
    @api.depends('approve_document')
    def _compute_submit_time(self):
        for rec in self:
            try:
                if rec.approve_document:
                    rec.submit_time = rec.approve_document.submit_time
            except Exception as e:
                pass

    @api.multi
    @api.depends('approve_document')
    def _compute_note(self):
        for rec in self:
            try:
                if rec.approve_document and rec.approve_document._name in self.env.registry.models:
                    try:
                        if hasattr(rec.approve_document, 'note'):
                            rec.document_note = rec.approve_document.note
                        elif hasattr(rec.approve_document, 'x_note'):
                            rec.document_note = rec.approve_document.x_note
                        else:
                            rec.document_note = ''
                    except Exception as e:
                        rec.document_note = ''
            except Exception as e:
                pass

    @api.multi
    def _compute_current_line_instance(self):
        for rec in self:
            instance_id = rec
            if instance_id.state == 'processing':
                rec.current_line_instance_id = instance_id.instance_line_ids.filtered(
                    lambda line: line.state == 'processing').id
            else:
                end_line = instance_id.instance_line_ids.filtered(
                    lambda line: line.workflow_line_id.node_type == 'end')
                if end_line:
                    rec.current_line_instance_id = end_line[0].id
                else:
                    res = self.env['workflow.instance.line'].search(
                        [('id', 'in', instance_id.instance_line_ids.ids)],
                        order='process_time desc', limit=1)
                    if res:
                        rec.current_line_instance_id = res.id

    @api.multi
    @api.depends('approve_document')
    def _compute_document_company(self):
        for rec in self:
            try:
                approve_document = rec.approve_document
                if approve_document:
                    approve_document = approve_document.suspend_security()
                    if approve_document._name in ('kthrp.hr.employee.change.apply', 'kthrp.hr.employee.change.resign',
                                                  'kthrp.hr.leave.record',
                                                  'kthrp.payroll.salary.adjustment.application',
                                                  'kthrp.payroll.salary.summary.payslip',
                                                  'kthrp.hr.overtime.application',
                                                  'kthrp.hr.bank.account.change',
                                                  'kthrp.hr.resumption.leave.application',
                                                  'kthrp.hr.outwork.application', 'kthrp.hr.overtime.application',
                                                  'kthrp.hr.bank.account.change',
                                                  'kthrp.hr.employee.change.further.study',
                                                  'kthrp.hr.employee.change.apply',
                                                  'kthrp.employee.information.report',
                                                  'kthrp.hr.contract.renewal.application',
                                                  'kthrp.hr.employment.contract',
                                                  'kthrp.hr.timesheet',
                                                  'kthrp.hr.employee.change.global.transfer',
                                                  'kthrp.hr.recruitment.application',
                                                  'kthrp.payroll.si.result',
                                                  'kthrp.payroll.special.salary.fee.application',
                                                  'kthrp.payroll.bonus.matter.application',
                                                  'kthrp.hr.chanyi.employee.hpf.purchase.application'):
                        try:
                            rec.company_id = approve_document.legal_company_id.id
                        except Exception as e:
                            pass
                    else:
                        try:
                            if hasattr(approve_document, 'company_id'):
                                rec.company_id = approve_document.company_id.id
                            elif hasattr(approve_document, 'x_company_id'):
                                rec.company_id = approve_document.x_company_id.id
                            elif hasattr(approve_document, 'x_company'):
                                rec.company_id = approve_document.x_company.id
                            else:
                                rec.company_id = False
                        except Exception as e:
                            pass
            except Exception as e:
                pass

    @api.multi
    def open_approve_document(self):
        self.ensure_one()
        self = self.suspend_security()
        ctx = self.env.context.copy()

        if self.approve_document:
            if self.approve_document._name in ['kthrp.expense.report', 'kthrp.contract.contract',
                                               'kthrp.payable.payment.request']:
                ctx.update({
                    # 'no_create': True,
                    # 'no_edit': True,
                    'no_delete': True,
                    'suspend_security': True,
                })
            else:
                ctx.update({
                    # 'no_create': True,
                    # 'no_edit': True,
                    'no_delete': True,
                    # 'no_action': True,
                    'suspend_security': True,
                })
            ctx.update({'active_model': self.approve_document._name, 'active_ids': self.approve_document.ids,
                        'active_id': self.approve_document.id})
            return self.with_context(ctx).approve_document.open_document()
        else:
            return False

    @api.multi
    def unlink(self):
        for instance in self:
            if instance.state != 'withdrawn':
                if not self.env.context.get('background_unlink_summary_payslips', False):
                    raise ValidationError(_("You can not delete the document for workflow record reference."))
        return super(WorkflowInstance, self).unlink()

    @api.model
    def _select_objects(self):
        sql = ''
        if self.env.lang == 'zh_CN':
            sql = '''select im.model_name as model, COALESCE(it.value, im.name) as name 
from workflow_model im
       left join ir_translation it on (it.name = 'ir.model,name' and it.lang = 'zh_CN' and res_id = im.model_id)'''
        else:
            sql = '''select model_name as model, name as name from workflow_model'''

        self._cr.execute(sql)
        sql_result = self._cr.dictfetchall()

        return [(record.get('model'), record.get('name')) for record in sql_result] + [('', '')]

        # records = self.env['ir.model'].search([])
        # return [(record.model, record.name) for record in records] + [('', '')]

    def init_exec(self, apply_employee_id=None):
        self = self.suspend_security()
        if self.state != 'processing':
            raise ValidationError(
                _('Error:Workflow instance state is not processing!'))
        transitions = self.workflow_id.workflow_line_ids.filtered(lambda t: t.is_start).out_transition_ids
        # 选出符合条件的下一节点
        model_instance_id = self.model_instance_id
        model_name = self.workflow_id.wkf_model_id.model
        transitions = sorted(transitions, key=lambda x: x.sequence)
        next_transition = None
        approve_document = self.approve_document
        task_obj = self.env['workflow.task']
        for rec in transitions:
            if not rec.wkf_condition_id:
                next_transition = rec
                break
            else:
                if rec.wkf_condition_id.check_match_domain(model_name, model_instance_id) is True:
                    next_transition = rec
                    break
                else:
                    pass
        if not next_transition:
            raise ValidationError(
                _('Error:Not found next transition!'))
        tasks = []
        employee_ids, auto_approve = self.get_approve_ids(next_transition.from_workflow_line_id,
                                            next_transition.to_workflow_line_id)
        x_auto_approve = False
        if next_transition.to_workflow_line_id.related_line_id:
            x_employee_ids = self.get_approve_ids_skip(next_transition.to_workflow_line_id.related_line_id)
            if employee_ids == x_employee_ids and x_employee_ids:
                x_auto_approve = True

        node_obj = self.env['workflow.instance.line']
        submit_signal = self.env['workflow.signal'].search([('code', '=', 'submit')], limit=1)

        from_node_instance = node_obj.create({'instance_id': self.id,
                                              'workflow_line_id': next_transition.from_workflow_line_id.id,
                                              # 'env_params': self._context,
                                              'process_time': fields.Datetime.now(),
                                              # 'task_ids': [(0, 0, {'approve_result_id': submit_signal.id,
                                              #                      'allocated_approver_id': apply_employee_id,
                                              #                      'actual_approver_id': apply_employee_id,
                                              #                      'approve_time': fields.Datetime.now()})],
                                              'state': 'processed'})
        task_from_vals = {'wkf_line_instance_id': from_node_instance.id,
                          'allocated_approver_id': self.env.user.related_employee_id.id,
                          'allocated_user_id': self.env.user.id,
                          'approve_result_id': submit_signal.id,
                          'approve_time': fields.Datetime.now(),
                          'approve_document': approve_document._name + ',' + str(approve_document.id),
                          'document_name': task_obj.get_document_name(approve_document) or '',
                          'wkf_line_instance_state': 'processed',
                          'wkf_model_id': self.wkf_model_id.id or None,
                          'model_instance_id': model_instance_id or None,
                          'submit_user_id': approve_document.submit_user_id.id or None,
                          'submit_employee_id':approve_document.submit_user_id.related_employee_id.id or None,
                          'submit_time': approve_document.submit_time or None,
                          'company_id': task_obj.get_document_company(approve_document) or None,
                          'note': task_obj.get_document_note(approve_document) or '',
                          'to_be_approved': True,
                          'reject_display': True,
                          'is_special_transfer': False,
                          'wkf_line_instance_node_type': 'start'

                          }
        if self.env.context.get('submit_note'):
            approve_note = self.env.context.get('submit_note')
        else:
            approve_note = False
        task_from_vals['approve_note'] = approve_note or ''
        task_id = task_obj.task_sql_create(task_from_vals, node_type='start')
        if not task_id:
            raise ValidationError(_('There is an approval error, please contact the sysadmin.'))

        to_node_instance = node_obj.create({'instance_id': self.id,
                                            'workflow_line_id': next_transition.to_workflow_line_id.id,
                                            # 'env_params': self._context,
                                            # 'task_ids': tasks
                                            })
        for employee_list in employee_ids:
            substitution_id = next_transition.to_workflow_line_id.check_substitution_rule(
                self, employee_list[0])
            allocated_user_id = self.env['res.users'].search([('related_employee_id', '=', substitution_id)],
                                                             limit=1)
            # new_task_vals = {'allocated_approver_id': substitution_id or employee_list[0]}
            if len(employee_ids) == 1 and (substitution_id or employee_list[0]) == self.env.user.related_employee_id.id:
                setting = self.env.ref('workflow.workflow_default_setting', raise_if_not_found=False)
                if setting.submitter_as_first_approver == 'auto_send_to_direct_leader':
                    allocated_approver_id = self.env.user.related_employee_id.manager_id.id or self.env.user.related_employee_id.id
                    allocated_user_id = self.env['res.users'].search([('related_employee_id', '=', allocated_approver_id)],
                                                                     limit=1)
                else:
                    allocated_approver_id = substitution_id or employee_list[0]
            else:
                allocated_approver_id = substitution_id or employee_list[0]

            new_task_vals = {'wkf_line_instance_id': to_node_instance.id,
                             'allocated_approver_id': allocated_approver_id,
                             'allocated_user_id': allocated_user_id.id or None,
                             'approve_document': approve_document._name + ',' + str(approve_document.id),
                             'document_name': task_obj.get_document_name(approve_document) or '',
                             'wkf_line_instance_state': to_node_instance.state,
                             'wkf_model_id': self.wkf_model_id.id or None,
                             'model_instance_id': self.model_instance_id or None,
                             'submit_user_id': approve_document.submit_user_id.id or None,
                             'submit_employee_id': approve_document.submit_user_id.related_employee_id.id or None,
                             'submit_time': approve_document.submit_time or '',
                             'company_id': task_obj.get_document_company(approve_document) or None,
                             'note': task_obj.get_document_note(approve_document) or '',
                             'to_be_approved': True,
                             'reject_display': True,
                             'is_special_transfer': False,
                             'wkf_line_instance_node_type': next_transition.to_workflow_line_id.node_type,
                             'auto_approve': auto_approve or x_auto_approve,
                             'skip_exception': auto_approve,

                             }
            if employee_list[1]:
                new_task_vals['authority_transfer'] = employee_list[1]
            to_task_id = task_obj.task_sql_create(new_task_vals)
            if not to_task_id:
                raise ValidationError(_('There is an approval error, please contact the sysadmin.'))
            # tasks.append((0, 0, new_task_vals))

        # 建立节点连接
        self.env['workflow.instance.line.transition'].create(
            {'transition_id': next_transition.id, 'from_instance_line_id': from_node_instance.id,
             'to_instance_line_id': to_node_instance.id})
        for task in from_node_instance.task_ids:
            task.transition_notify()
        for task in to_node_instance.task_ids:
            task.compute_last_wkf_line_instance_id()
            if task.allocated_approver_id:
                task._wkf_send_email('need_approval')

    def start(self):
        # 此时有且只有一个可执行的activity
        self = self.suspend_security()
        nodes = [node for node in self.instance_line_ids if not node.is_start and not node.is_end]
        approve_document = self.approve_document
        task_obj = self.env['workflow.task']
        model_instance_id = self.model_instance_id
        if len(nodes) != 1:
            if len(nodes) == 0 and len(self.instance_line_ids) != 0:
                end_signal = self.env['workflow.signal'].search([('code', '=', 'complete')], limit=1)
                nodes_end = [node for node in self.instance_line_ids if node.is_end and not node.is_start]
                if nodes_end:
                    nodes_end[0].write({'process_time': fields.Datetime.now(),
                                        'state': 'processed'})
                                        # 'task_ids': [(0, 0, {
                                        #     'approve_result_id': end_signal.id,
                                        #     'approve_time': fields.Datetime.now()})]})
                    task_from_vals = {'wkf_line_instance_id': nodes_end[0].id,
                                      'allocated_approver_id': self.env.user.related_employee_id.id,
                                      'allocated_user_id': self.env.user.id,
                                      'approve_result_id': end_signal.id,
                                      'approve_time': fields.Datetime.now(),
                                      'approve_document': approve_document._name + ',' + str(approve_document.id),
                                      'document_name': task_obj.get_document_name(approve_document) or '',
                                      'wkf_line_instance_state': 'processed',
                                      'wkf_model_id': self.wkf_model_id.id or None,
                                      'model_instance_id': model_instance_id or None,
                                      'submit_user_id': approve_document.submit_user_id.id or None,
                                      'submit_employee_id': approve_document.submit_user_id.related_employee_id.id or None,
                                      'submit_time': approve_document.submit_time or None,
                                      'company_id': task_obj.get_document_company(approve_document) or None,
                                      'note': task_obj.get_document_note(approve_document) or '',
                                      'to_be_approved': True,
                                      'reject_display': True,
                                      'is_special_transfer': False
                                      }
                    task_id = task_obj.task_sql_create(task_from_vals, node_type='start')
                    if not task_id:
                        raise ValidationError(_('There is an approval error, please contact the sysadmin.'))
                self.env[self.workflow_id.wkf_model_id.model].complete_approve(self.model_instance_id)
                self.complete()
                self.write({'state': 'completed'})
                for act in self.instance_line_ids:
                    act.write({'state': 'processed', 'process_time': fields.Datetime.now()})
            else:
                raise ValidationError(_('There isn’t next approval node, please contact the sysadmin.'))
        else:
            self.write({'state': 'processing'})
            nodes[0].process()

    # 添加下一节点
    def add_next_activity(self, transition, current_node_instance, skip=False):
        self = self.suspend_security()

        next_node = transition.to_workflow_line_id
        node_instance_obj = self.env['workflow.instance.line']
        task_obj = self.env['workflow.task']
        approve_document = current_node_instance.instance_id.approve_document
        employee_sql = '''select related_employee_id FROM res_users WHERE id=%s'''
        self.env.cr.execute(employee_sql, (approve_document.submit_user_id.id,))
        related_employee_id = self.env.cr.fetchone()[0]
        setting = self.env.ref('workflow.workflow_default_setting', raise_if_not_found=False)
        if next_node.is_end:
            end_signal = self.env['workflow.signal'].search([('code', '=', 'complete')], limit=1)
            end_node_instance = node_instance_obj.with_context(tracking_disable=True).create({'instance_id': self.id,
                                                                                              'workflow_line_id': next_node.id,
                                                                                              # 'env_params': self._context,
                                                                                              'process_time': fields.Datetime.now(),
                                                                                              'state': 'processed'})
            task_end_vals = {'wkf_line_instance_id': end_node_instance.id,
                             'approve_result_id': end_signal.id,
                             'approve_time': fields.Datetime.now(),
                             'approve_document': approve_document._name + ',' + str(approve_document.id),
                             'document_name': task_obj.get_document_name(approve_document) or '',
                             'wkf_line_instance_state': 'processed',
                             'wkf_model_id': current_node_instance.instance_id.wkf_model_id.id or None,
                             'model_instance_id': current_node_instance.instance_id.model_instance_id or None,
                             'submit_user_id': approve_document.submit_user_id.id or None,
                             'submit_employee_id': related_employee_id or None,
                             'submit_time': approve_document.submit_time or None,
                             'company_id': task_obj.get_document_company(approve_document) or None,
                             'note': task_obj.get_document_note(approve_document) or '',
                             'to_be_approved': True,
                             'reject_display': True,
                             'is_special_transfer': False,
                             'wkf_line_instance_node_type': 'end'
                             }
            # task_obj.create(task_end_vals)
            task_id = task_obj.task_sql_create(task_end_vals, node_type='end')
            if not task_id:
                raise ValidationError(_('There is an approval error, please contact the sysadmin.'))
            # 建立节点连接
            self.env['workflow.instance.line.transition'].create(
                {'transition_id': transition.id, 'from_instance_line_id': current_node_instance.id,
                 'to_instance_line_id': end_node_instance.id})
            for task in end_node_instance.task_ids:
                task.compute_last_wkf_line_instance_id()
            if skip:
                # 最后一步结束
                self.env[self.workflow_id.wkf_model_id.model].complete_approve(self.model_instance_id)
                self.complete()
            return end_node_instance
        else:
            # 添加
            tasks = []
            x_auto_approve = False
            employee_ids, auto_approve = self.get_approve_ids(current_node_instance, next_node)
            if next_node.related_line_id:
                x_employee_ids = self.get_approve_ids_skip(next_node.related_line_id)
                if employee_ids == x_employee_ids and x_employee_ids:
                    x_auto_approve = True

            next_node_instance = node_instance_obj.with_context(tracking_disable=True).create({'instance_id': self.id,
                                                                                               'workflow_line_id': next_node.id,
                                                                                               # 'env_params': self._context,
                                                                                               #  'task_ids': tasks,
                                                                                               })

            only_actual_approver_id = None
            for employee_list in employee_ids:
                substitution_id = next_node.check_substitution_rule(
                    self, employee_list[0])
                only_actual_approver_id = substitution_id
                allocated_user_id = self.env['res.users'].search([('related_employee_id', '=', substitution_id)],
                                                                 limit=1)
                new_task_vals = {'wkf_line_instance_id': next_node_instance.id,
                                 'allocated_approver_id': substitution_id or employee_list[0],
                                 'allocated_user_id': allocated_user_id.id or None,
                                 'approve_document': approve_document._name + ',' + str(approve_document.id),
                                 'document_name': task_obj.get_document_name(approve_document) or '',
                                 'wkf_line_instance_state': next_node_instance.state,
                                 'wkf_model_id': current_node_instance.instance_id.wkf_model_id.id or None,
                                 'model_instance_id': current_node_instance.instance_id.model_instance_id or None,
                                 'submit_user_id': approve_document.submit_user_id.id or None,
                                 'submit_employee_id': related_employee_id,
                                 'submit_time': approve_document.submit_time or '',
                                 'company_id': task_obj.get_document_company(approve_document) or None,
                                 'note': task_obj.get_document_note(approve_document) or '',
                                 'to_be_approved': True,
                                 'reject_display': True,
                                 'is_special_transfer': False,
                                 'wkf_line_instance_node_type': next_node.node_type,
                                 'auto_approve': auto_approve or x_auto_approve,
                                 'skip_exception': auto_approve,
                                 }
                if employee_list[1]:
                    new_task_vals['authority_transfer'] = employee_list[1]
                task_id = task_obj.task_sql_create(new_task_vals)
                if not task_id:
                    raise ValidationError(_('There is an approval error, please contact the sysadmin.'))
                    # tasks.append((0, 0, new_task_vals))

            # 建立节点连接
            self.env['workflow.instance.line.transition'].create(
                {'transition_id': transition.id, 'from_instance_line_id': current_node_instance.id,
                 'to_instance_line_id': next_node_instance.id})
            for task in next_node_instance.task_ids:
                task.compute_last_wkf_line_instance_id()
            skip_add_next_task = False
            new_next_node_instance = None
            # 存在非相邻审批节点跳过
            if not next_node.non_adj_repeat_approve and len(employee_ids) == 1 and only_actual_approver_id:
                before_tasks = task_obj.search_count([('wkf_line_instance_id','not in', [current_node_instance.id, next_node_instance.id]),
                                                      ('approve_result_id', '!=', False),
                                                      ('approve_result_id.code', '=', 'approve'),
                                                      ('actual_approver_id', '=', only_actual_approver_id),
                                                      ('wkf_line_instance_state', '!=', 'processing'),
                                                      ('wkf_line_instance_id.instance_id', '=', current_node_instance.instance_id.id)])
                if before_tasks and next_node_instance.task_ids:
                    next_node_instance.task_ids.write({'auto_approve': True})
                    signal = self.env['workflow.signal'].search([('code', '=', 'approve')])
                    next_node_instance.approve('approve', signal, next_node_instance.task_ids[0])

                    transitions = []
                    for x_transition in [t for t in next_node.out_transition_ids]:
                        transitions.append(x_transition)
                    model_instance_id = self.model_instance_id
                    model_name = self.workflow_id.wkf_model_id.model
                    transitions = sorted(transitions, key=lambda x: x.sequence)
                    next_transition = []
                    for rec in transitions:
                        if not rec.wkf_condition_id:
                            next_transition = rec
                            break
                        else:
                            if rec.wkf_condition_id.check_match_domain(model_name, model_instance_id) is True:
                                next_transition = rec
                                break
                            else:
                                pass
                    if len(next_transition) != 1:
                        raise ValidationError(_('There isn’t next approval node, please contact the sysadmin.'))
                    else:
                        transitions = []
                        transitions.append(next_transition)
                    x_transition = None
                    if len(transitions) == 0:
                        raise ValidationError(_('There isn’t next approval node, please contact the sysadmin.'))
                    elif len(transitions) > 1:
                        raise ValidationError(
                            _('Error:Find more than one activity!'))
                    else:
                        x_transition = transitions[0]

                    next_node_instance.task_ids.write({'approve_note':_('Non adjacent approved nodes, skip automatically.')})
                    for task in next_node_instance.task_ids:
                        task.transition_notify()

                    skip_add_next_task = True
                    new_next_node_instance = self.add_next_activity(x_transition, next_node_instance, skip=True)
            # 配置关联节点后面跳过已经提交人配置不审批后跳过
            submit_auto_approve = False
            if len(employee_ids) == 1 and setting.skip_submitter_approve and only_actual_approver_id == approve_document.submit_employee_id.id:
                if next_node.non_adj_repeat_approve or next_node.repeat_approve:
                    submit_auto_approve = False
                else:
                    submit_auto_approve = True
            if (auto_approve or x_auto_approve or submit_auto_approve) and len(employee_ids) == 1 and next_node_instance.task_ids:
                signal = self.env['workflow.signal'].search([('code', '=', 'approve')])
                if submit_auto_approve:
                    next_node_instance.task_ids.write({'auto_approve': True})
                next_node_instance.approve('approve', signal, next_node_instance.task_ids[0])

                transitions = []
                for x_transition in [t for t in next_node.out_transition_ids]:
                    transitions.append(x_transition)
                model_instance_id = self.model_instance_id
                model_name = self.workflow_id.wkf_model_id.model
                transitions = sorted(transitions, key=lambda x: x.sequence)
                next_transition = []
                for rec in transitions:
                    if not rec.wkf_condition_id:
                        next_transition = rec
                        break
                    else:
                        if rec.wkf_condition_id.check_match_domain(model_name, model_instance_id) is True:
                            next_transition = rec
                            break
                        else:
                            pass
                if len(next_transition) != 1:
                    raise ValidationError(_('There isn’t next approval node, please contact the sysadmin.'))
                else:
                    transitions = []
                    transitions.append(next_transition)
                x_transition = None
                if len(transitions) == 0:
                    raise ValidationError(_('There isn’t next approval node, please contact the sysadmin.'))
                elif len(transitions) > 1:
                    raise ValidationError(
                        _('Error:Find more than one activity!'))
                else:
                    x_transition = transitions[0]
                if self.has_install_module('kthrp_szxys_ext'):
                    comment = _('The application has been processed subsequently!')
                else:
                    comment = _('Auto Approve.')
                if x_auto_approve:
                    comment = _('Repeat the skip with subsequent nodes.')
                elif submit_auto_approve:
                    comment = _('Submitter No Need Approval.')
                next_node_instance.task_ids.write(
                    {'approve_note': comment})
                for task in next_node_instance.task_ids:
                    task.transition_notify()

                if not skip_add_next_task:
                    new_next_node_instance = self.add_next_activity(x_transition, next_node_instance, skip=True)
            if new_next_node_instance and skip_add_next_task:
                new_next_node_instance.task_ids._wkf_send_email('need_approval')

            return next_node_instance

    # 获取审批人
    def get_approve_ids(self, current_node, next_node):
        self = self.suspend_security()
        records_obj = self.env['workflow.batch.transfer.records']
        approve_ids = []
        auto_approve = False
        wkf_role_obj = self.env['workflow.role']
        if next_node.is_start or next_node.is_end:
            return approve_ids , False
        if next_node.approver_type == 'approve_role':
            for approver in wkf_role_obj.get_role_approver(self, next_node):
                approve_ids.append(approver.id)
        elif next_node.approver_type == 'related_person':
            related_approver = None
            approver = None
            if next_node.approver_source == 'submitter':
                # 申请人
                related_approver = self.apply_employee_id
            elif next_node.approver_source == 'last_approver':
                # 最后审批人
                related_approver = current_node.get_last_approver()
            elif next_node.approver_source == 'model_person':
                related_field = next_node.related_field.name
                object_model = self.env[self.workflow_id.wkf_model_id.model].browse(self.model_instance_id)
                if object_model:
                    related_approver = getattr(object_model, related_field)
            if related_approver:
                if next_node.related_person == 'manager':
                    approver = related_approver.manager_id
                elif next_node.related_person == 'department_head':
                    approver = related_approver.department_id.dept_manager_id
                elif next_node.related_person == 'parent_position':
                    position_id = related_approver.position_id
                    if position_id:
                        parent_position = position_id.superior_position_id
                        if parent_position:
                            approver = self.env['hr.employee'].search([('position_id', '=', parent_position.id)])
            if approver:
                approve_ids += approver.ids
        elif next_node.approver_type == 'designated_person':
            designated_ids = self.env.context.get('designated_approver', None)
            if designated_ids:
                approve_ids += designated_ids
            else:
                raise ValidationError(
                    _("Document need be designated approver."))
        elif next_node.approver_type == 'designated_position':
            position_employee = self.env['hr.employee'].search([('position_id', '=', next_node.position_id.id)])
            approve_ids += position_employee.ids
        elif next_node.approver_type == 'dynamic_selection':
            select_approver =  self.env.context.get('select_approver')
            if select_approver:
                approve_ids.append(select_approver)
        # if current_node._name == 'workflow.instance.line':
        #     current_wkf_line = current_node.workflow_line_id
        # else:
        #     current_wkf_line = current_node
        if not approve_ids and next_node.approve_role_id.transfer_id:
            approve_ids += next_node.approve_role_id.transfer_id.ids
            if next_node.approve_role_id.auto_approve_when_no:
                auto_approve = True
        if len(approve_ids) == 0:
            raise ValidationError(
                _('There isn’t approver in the next approval node, please contact the sysadmin.'))
        temp_approve_ids = []
        for approve_id in approve_ids:
            record = records_obj.get_transfer_record(self.workflow_id.wkf_model_id.id, approve_id)
            if record:
                temp_approve_ids += records_obj.transfer_employee(approve_id, record)
            else:
                temp_approve_ids += [[approve_id, '']]
        return temp_approve_ids, auto_approve

    def get_approve_ids_skip(self, next_node):
        self = self.suspend_security()
        records_obj = self.env['workflow.batch.transfer.records']
        approve_ids = []
        wkf_role_obj = self.env['workflow.role']
        if next_node.is_start or next_node.is_end:
            return approve_ids
        if next_node.approver_type == 'approve_role':
            for approver in wkf_role_obj.get_role_approver(self, next_node):
                approve_ids.append(approver.id)
        elif next_node.approver_type == 'related_person':
            related_approver = None
            approver = None
            if next_node.approver_source == 'submitter':
                # 申请人
                related_approver = self.apply_employee_id
            elif next_node.approver_source == 'last_approver':
                # 最后审批人
                related_approver = self.env['hr.employee']
            elif next_node.approver_source == 'model_person':
                related_field = next_node.related_field.name
                object_model = self.env[self.workflow_id.wkf_model_id.model].browse(self.model_instance_id)
                if object_model:
                    related_approver = getattr(object_model, related_field)
            if related_approver:
                if next_node.related_person == 'manager':
                    approver = related_approver.manager_id
                elif next_node.related_person == 'department_head':
                    approver = related_approver.department_id.dept_manager_id
                elif next_node.related_person == 'parent_position':
                    position_id = related_approver.position_id
                    if position_id:
                        parent_position = position_id.superior_position_id
                        if parent_position:
                            approver = self.env['hr.employee'].search([('position_id', '=', parent_position.id)])
            if approver:
                approve_ids += approver.ids
        elif next_node.approver_type == 'designated_person':
            designated_ids = self.env.context.get('designated_approver', None)
            if designated_ids:
                approve_ids += designated_ids
            else:
                raise ValidationError(
                    _("Document need be designated approver."))
        elif next_node.approver_type == 'designated_position':
            position_employee = self.env['hr.employee'].search([('position_id', '=', next_node.position_id.id)])
            approve_ids += position_employee.ids
        # if len(approve_ids) == 0:
        #     raise ValidationError(
        #         _('There isn’t approver in the next approval node, please contact the sysadmin.'))
        temp_approve_ids = []
        for approve_id in approve_ids:
            record = records_obj.get_transfer_record(self.workflow_id.wkf_model_id.id, approve_id)
            if record:
                temp_approve_ids += records_obj.transfer_employee(approve_id, record)
            else:
                temp_approve_ids += [[approve_id, '']]
        return temp_approve_ids

    @api.model
    def for_transfer(self, approve_ids):
        result = []
        for rec in approve_ids:
            result.append([rec, ''])
        return result

    # 向前推进一步
    def process(self, signal_type, task):
        self = self.suspend_security()
        # 获取当前节点(activity)和下一步路径(transition)
        current_node_instance = self.get_current_node_instance()
        # 停留:会签节点,点击同意,当前activity还有其他人没有审批
        # 向前:其他情况
        current_node = current_node_instance.workflow_line_id
        if signal_type != 'reject':
            # 获取下一步路径(transition)
            transitions = []
            for transition in [t for t in current_node.out_transition_ids if
                               signal_type in t.from_workflow_line_id.signal_type_ids.mapped('code')]:
                transitions.append(transition)
            model_instance_id = self.model_instance_id
            model_name = self.workflow_id.wkf_model_id.model
            transitions = sorted(transitions, key=lambda x: x.sequence)
            next_transition = []
            for rec in transitions:
                if not rec.wkf_condition_id:
                    next_transition = rec
                    break
                else:
                    if rec.wkf_condition_id.check_match_domain(model_name, model_instance_id) is True:
                        next_transition = rec
                        break
                    else:
                        pass
            if len(next_transition) != 1:
                raise ValidationError(_('There isn’t next approval node, please contact the sysadmin.'))
            else:
                transitions = []
                transitions.append(next_transition)
            transition = None
            if len(transitions) == 0:
                raise ValidationError(_('There isn’t next approval node, please contact the sysadmin.'))
            elif len(transitions) > 1:
                raise ValidationError(
                    _('Error:Find more than one activity!'))
            else:
                transition = transitions[0]

            # 当前节点执行审批
            signal = self.env['workflow.signal'].search([('code', '=', signal_type)])
            # 节点处理前触发
            self.approve_document.workflow_task_approve(self.workflow_id, self.approve_document, current_node_instance)
            current_node_instance.approve(signal_type, signal, task)
        else:
            reject_signal = self.env['workflow.signal'].search(
                [('code', '=', 'reject')], limit=1)

            # 当前节点执行审批拒绝
            current_node_instance.approve(signal_type, reject_signal, task)

        if signal_type == 'approve':
            # 通过
            # 判断是否会签
            is_special_transfer, from_task_id = task.get_special_transfer_and_task()
            if not task.check_linkup_or_add_processed():
                pass
            elif current_node_instance.countersign and not current_node_instance.all_approved() or is_special_transfer:
                pass
            else:
                next_node_instance = self.add_next_activity(transition, current_node_instance)
                next_node = next_node_instance.workflow_line_id
                for next_task in next_node_instance.task_ids:
                    before_tasks = self.env['workflow.task'].search_count(
                        [('wkf_line_instance_id', 'not in', [current_node_instance.id, next_node_instance.id]),
                         ('approve_result_id', '!=', False),
                         ('approve_result_id.code', '=', 'approve'),
                         ('actual_approver_id', '=', next_task.allocated_approver_id.id),
                         ('wkf_line_instance_state', '!=', 'processing'),
                         ('wkf_line_instance_id.instance_id', '=', current_node_instance.instance_id.id)])
                    # 非相邻已审批节点，跳过不发送消息
                    if not next_node.non_adj_repeat_approve and before_tasks:
                        special = True
                    elif next_node.repeat_approve or next_task.allocated_approver_id.id != task.actual_approver_id.id or next_node.approver_type == 'designated_person':
                        special = False
                    else:
                        special = True
                    if next_task.allocated_approver_id and not special:
                        next_task._wkf_send_email('need_approval')
                if next_node_instance.is_end:
                    # 最后一步结束
                    self.env[self.workflow_id.wkf_model_id.model].complete_approve(self.model_instance_id)
                    self.complete()
                else:
                    next_node_instance.process()
        elif signal_type == 'reject':
            # self.add_next_activity(transition, current_activity_instance)
            self.reject()
            self.env[self.workflow_id.wkf_model_id.model].refuse_approve(self.model_instance_id)

    # 获取当前节点
    def get_current_node_instance(self):
        processing_nodes = []
        for line in self.instance_line_ids:
            line.invalidate_cache()
            if line.state == 'processing':
                processing_nodes.append(line)

        if len(processing_nodes) != 1:
            raise ValidationError(
                _('Error:Workflow activity is not found!'))
        else:
            return processing_nodes[0]

    def reject(self):
        self.approve_document.workflow_rejected()
        saved_document_obj = self.env['workflow.saved.document']
        saved_document_obj.delete_saved_document(self.approve_document._name, self.approve_document.id)
        self.write({'state': 'rejected'})
        self.finish_and_check()

    def complete(self):
        saved_document_obj = self.env['workflow.saved.document']
        saved_document_obj.delete_saved_document(self.approve_document._name, self.approve_document.id)
        self.approve_document.workflow_approved()
        self.write({'state': 'completed'})
        self.finish_and_check()

    @api.one
    def callback(self):
        self.write({'state': 'withdrawn'})
        for line in self.instance_line_ids.filtered(lambda x: x.state == 'processing'):
            line.callback()
        self.finish_and_check()

    def finish_and_check(self):
        if self.workflow_id.auto_inactive:
            sql = '''SELECT count(*) FROM workflow_instance WHERE state='processing' AND workflow_id = %s'''
            self.env.cr.execute(sql, (self.workflow_id.id,))
            count = self.env.cr.fetchone()[0]
            if not count:
                self.workflow_id.active = False


class WorkflowInstanceLine(models.Model):
    _name = "workflow.instance.line"
    _description = "Kthrp Base Workflow Instance Line"
    _going_live_clear_cls = 'clearable'
    _rec_name = 'workflow_line_id'

    instance_id = fields.Many2one(comodel_name='workflow.instance', string='Instance',
                                  ondelete="cascade", index=True)
    workflow_line_id = fields.Many2one(comodel_name='workflow.line', string='Workflow Node', index=True)
    # name = fields.Char(string='Node Name', compute="_get_transition_name")
    process_time = fields.Datetime(string='Process Time')
    countersign = fields.Boolean(string='Countersign', related='workflow_line_id.countersign', readonly=True, )
    in_transition_ids = fields.One2many(comodel_name='workflow.instance.line.transition',
                                        inverse_name='to_instance_line_id',
                                        string='To Instance Transition')
    out_transition_ids = fields.One2many(comodel_name='workflow.instance.line.transition',
                                         inverse_name='from_instance_line_id',
                                         string='From Instance Transition')
    task_ids = fields.One2many(comodel_name='workflow.task', inverse_name='wkf_line_instance_id',
                               string='Tasks')
    state = fields.Selection(string="Process State",
                             selection=[('processing', 'Processing'),
                                        ('processed', 'Processed'),
                                        ('returned', 'Returned'),
                                        ('withdrawn', 'Withdrawn')],
                             required=True,
                             index=True,
                             default='processing',
                             track_visibility='onchange')

    # 获取最后审批人
    def get_last_approver(self):
        approvers = self.env['workflow.task'].search(
            [('wkf_line_instance_id', '=', self.id), ('actual_approver_id', '!=', False)], order='approve_time desc')
        if len(approvers) > 0:
            return approvers[0].actual_approver_id
        else:
            return None

    @property
    def is_start(self):
        return self.workflow_line_id.is_start

    @property
    def is_end(self):
        return self.workflow_line_id.is_end

    @api.multi
    def _get_transition_name(self):
        if self.env.context.get('from_task_id'):
            from_task = self.env['workflow.task'].browse(self.env.context.get('from_task_id'))
            approve_result = from_task.with_context(prefetch_fields=False).approve_result_id
            if approve_result.code in ['transfer', 'linkup', 'add']:
                for rec in self:
                    rec.name = ' '
            else:
                for rec in self:
                    rec.name = rec.workflow_line_id.name
        else:
            for rec in self:
                rec.name = rec.workflow_line_id.name

    def process(self):
        # 给用户发通知
        if self.state == 'returned' or self.state == 'withdrawn':
            self.state = 'processing'

    # 判断是否全部审批完
    def all_approved(self):
        not_approved = self.task_ids.filtered(lambda x: not x.approve_result_id)
        if not_approved:
            return False
        else:
            return True
            # task_len = len(self.task_ids)
            # approved_len = len([task for task in self.task_ids if task.approve_result_id])
            # if task_len > approved_len:
            #     return False
            # else:
            #     return True

    # 节点实例回撤
    @api.one
    def callback(self):
        if self.workflow_line_id.node_type != 'start':
            self.write({'state': 'withdrawn', 'process_time': fields.Datetime.now()})
            task = self.task_ids[0]
            task.callback()
            for d_task in self.task_ids.filtered(lambda x: x.id != task.id):
                d_task.unlink()

    def process_countersign_person(self, task):
        pass

    def process_or_sign(self, task):
        pass

    def process_reject_person(self, task):
        pass

    def process_transfer_person(self, task):
        pass

    def approve(self, signal_type, signal, task):
        # 更新tasks
        task.signal(signal)
        if self.state == 'processing':
            update_sql = '''update workflow_instance_line set state=%s,process_time WHERE id=%s'''
            if signal_type == 'approve':
                is_special_transfer, from_task_id = task.get_special_transfer_and_task()
                if not is_special_transfer:
                    if not task.check_linkup_or_add_processed():
                        pass
                    elif self.workflow_line_id.countersign and not self.all_approved():
                        self.process_countersign_person(task)
                    else:
                        update_sql = '''update workflow_instance_line set state=%s,process_time=%s WHERE id=%s'''
                        self._cr.execute(update_sql, ('processed', fields.Datetime.now(), self.id,))
                        update_sql_task = '''update workflow_task set wkf_line_instance_state=%s WHERE wkf_line_instance_id=%s'''
                        self._cr.execute(update_sql_task, ('processed', self.id,))
                        # self.write({'state': 'processed', 'process_time': fields.Datetime.now()})
                        self.process_or_sign(task)
                else:
                    self.process_transfer_person(task)
            else:
                update_sql = '''update workflow_instance_line set state=%s,process_time=%s WHERE id=%s'''
                self._cr.execute(update_sql, ('processed', fields.Datetime.now(), self.id,))
                update_sql_task = '''update workflow_task set wkf_line_instance_state=%s WHERE wkf_line_instance_id=%s'''
                self._cr.execute(update_sql_task, ('processed', self.id,))
                # self.write({'state': 'processed', 'process_time': fields.Datetime.now()})
                self.process_reject_person(task)
            self.invalidate_cache()

    @api.multi
    def create(self, vals):
        res = super(WorkflowInstanceLine, self).create(vals)
        if res.is_end:
            res.end_notify()
        return res

    @api.multi
    def end_notify(self):
        self.ensure_one()
        domain = [('wkf_line_instance_id.instance_id', '=', self.instance_id.id),
                  ('wkf_line_instance_id', '!=', self.id),
                  ('wkf_line_instance_node_type', '=', 'approve'),
                  ('wkf_line_instance_id.workflow_line_id.notify_by_end', '=', True)
                  ]
        tasks = self.env['workflow.task'].search(domain)
        if not tasks:
            return
        for task in tasks:
            task = task.sudo()
            task.with_context({'partner_ids': task.actual_approver_id,
                               'lang': self.env.context.get('lang', 'en_US')})._wkf_send_email('notify')

    @api.multi
    def name_get(self):
        result = []
        if self.env.context.get('from_task_id'):
            from_task = self.env['workflow.task'].browse(self.env.context.get('from_task_id'))
            approve_result = from_task.with_context(prefetch_fields=False).approve_result_id
            if approve_result.code in ['transfer', 'linkup', 'add']:
                for rec in self:
                    complete_name = '  '
                    result.append((rec.id, complete_name))
                return result
            else:
                return super(WorkflowInstanceLine, self).name_get()
        else:
            return super(WorkflowInstanceLine, self).name_get()


class WorkflowInstanceLineTransition(models.Model):
    _name = "workflow.instance.line.transition"
    _description = "Kthrp Base Workflow Instance Line Transition"
    _going_live_clear_cls = 'clearable'

    to_instance_line_id = fields.Many2one(comodel_name='workflow.instance.line',
                                          string='Target Node Instance',
                                          ondelete="cascade", index=True)
    from_instance_line_id = fields.Many2one(comodel_name='workflow.instance.line',
                                            string='Source Node Instance',
                                            ondelete="cascade", index=True)
    transition_id = fields.Many2one(comodel_name='workflow.line.transition', string='Transition')


# signal 方法换为sql更新
# 审批同意时 task通过 task_sql_create创建，后续store=True的计算和related通用字段请找到调用位置添加
class WorkflowTask(models.Model):
    _name = "workflow.task"
    _description = "Kthrp Base Workflow Task"
    _inherit = ['ir.needaction_mixin', 'workflow.interface']
    _rec_name = "wkf_line_instance_id"
    _going_live_clear_cls = 'clearable'

    wkf_line_instance_id = fields.Many2one(comodel_name="workflow.instance.line",
                                           string="Node Name",
                                           ondelete="cascade", auto_join=True, required=True, index=True)
    wkf_line_instance_name = fields.Char(compute='_compute_wkf_line_instance_name', readonly=True, store=True, string="Node Name")


    # 审批状态
    instance_approve_state = fields.Selection(compute='_compute_current_line_instance', readonly=True,
                                              selection=[('processing', 'Processing'),
                                                         ('completed', 'Completed'),
                                                         ('rejected', 'Rejected'),
                                                         ('withdrawn', 'Withdrawn')],
                                              string="Approve State")
    current_line_instance_id = fields.Many2one(comodel_name="workflow.instance.line",
                                               string="Current Node Name", compute='_compute_current_line_instance')
    wkf_line_instance_state = fields.Selection(related='wkf_line_instance_id.state', readonly=True, store=True, index=True)
    wkf_line_instance_node_type = fields.Selection(related='wkf_line_instance_id.workflow_line_id.node_type', readonly=True, store=True, index=True)

    approve_document = fields.Reference(related='wkf_line_instance_id.instance_id.approve_document',
                                        string='Approve Document', store=True, readonly=True)
    document_name = fields.Char(string='Approve Document', compute='_compute_document_name', store=True, index=True)
    last_wkf_line_instance_id = fields.Many2one(comodel_name="workflow.instance.line",
                                                string="Last Node")
    allocated_approver_id = fields.Many2one(comodel_name="hr.employee", string="Allocated Approver", index=True)
    allocated_user_id = fields.Many2one(comodel_name="res.users", compute='_compute_allocated_user_id', store=True)
    actual_approver_id = fields.Many2one(comodel_name="hr.employee", string="Actual Approver")
    actual_user_id = fields.Many2one(comodel_name="res.users", compute='_compute_actual_user_id', index=True, store=True)
    approve_result_id = fields.Many2one(comodel_name='workflow.signal', string='Approval Result', index=True)
    approve_time = fields.Datetime(string='Approval Time')
    approve_note = fields.Text(string='Approval Note')
    approve_attachments_ids = fields.Many2many(comodel_name='ir.attachment', string='Approval Attachments')
    last_approver_id = fields.Many2one(comodel_name="hr.employee", string="Last Approver")
    last_approve_time = fields.Datetime(string='Last Approve Time')
    to_be_approved = fields.Boolean(string='To be approved', default=True)
    wkf_model_id = fields.Many2one(comodel_name="ir.model", string="Document Model",
                                   related="wkf_line_instance_id.instance_id.wkf_model_id", store=True, readonly=True)
    model_instance_id = fields.Integer(string="Model Instance",
                                       related="wkf_line_instance_id.instance_id.model_instance_id", store=True,
                                       readonly=True)

    notify_ids = fields.Many2many(comodel_name="hr.employee")

    has_signal = fields.Boolean(compute='_compute_has_signal')
    is_admin = fields.Boolean(compute='_compute_has_signal')
    submit_user_id = fields.Many2one(comodel_name='res.users', compute="_compute_submit_info",
                                     store=True)
    submit_time = fields.Datetime(compute="_compute_submit_info", store=True)
    document_model_id = fields.Many2one(comodel_name='ir.model', string='Document Model',
                                        compute='_compute_document_model')
    company_id = fields.Many2one(comodel_name='res.company', string='Company', compute="_compute_document_company",
                                 store=True)
    reject_display = fields.Boolean(default=True)
    is_special_transfer = fields.Boolean(default=False)
    from_task_id = fields.Many2one(comodel_name='workflow.task')
    note = fields.Char(string='Document Note', compute='_compute_note', store=True)
    authority_transfer = fields.Char(string='Approval Transfer',
                                     readonly=1)
    transfer_to_id = fields.Many2one('hr.employee', string='Transfer To')

    doc_amount = fields.Char(string='Doc. Amount', compute='_compute_doc_amount', store=True)

    create_date = fields.Datetime(index=True)

    signature_image = fields.Binary(string='Approver Signature')

    fuzzy = fields.Char(prefetch=False)
    auto_approve = fields.Boolean()
    skip_exception = fields.Boolean()
    other_key_matter = fields.Char(string='Other Key Matter', compute='_compute_other_matter')
    doc_state = fields.Char(string='Document State', compute='_compute_doc_state')
    had_reminded = fields.Integer()

    urgency = fields.Selection(selection=[('1', 'General'),
                                          ('0', 'Urgent')], string='Urgency')

    # oa消息记录，用于更新oa消息状态:  oa_task_id + '-' + config_id
    dingding_msg_oa = fields.Char()

    notifier = fields.Many2many('hr.employee', string='Notifier')
    department_id = fields.Many2one(comodel_name='hr.department', string='Department',
                                    compute='_compute_department_id', store=True)

    @api.multi
    @api.depends('approve_document')
    def _compute_department_id(self):
        department_fields = ['department_id', 'apply_dept_id', 'application_dept_id', 'x_department',
                             'recruitment_department_id', 'x_application_department','expense_dept_id']
        for rec in self:
            try:
                if rec.approve_document and rec.approve_document._name in self.env.registry.models:
                    for x in department_fields:
                        try:
                            if hasattr(rec.approve_document, x):
                                department_id = getattr(rec.approve_document, x).id if getattr(rec.approve_document,
                                                                                                   x) else False
                                if department_id:
                                    rec.department_id = department_id
                                    break
                        except Exception as e:
                            rec.department_id = False
            except Exception as e:
                pass

    @api.multi
    def get_special_value(self, approve_document):
        # result = super(WorkflowTask, self).get_special_value(approve_document)
        result = {}
        model_name, record_id = approve_document.split(',')
        if model_name not in URGENCY_MODEL:
            return result
        record = self.env[model_name].browse(int(record_id))
        if record and hasattr(record, 'urgency_id'):
            if record.urgency_id == 'general':
                result['urgency'] = '1'
            elif record.urgency_id == 'urgent':
                result['urgency'] = '0'
            else:
                result['urgency'] = '1'
        else:
            result['urgency'] = '1'
        return result

    @api.multi
    def get_task_urgency(self):
        self = self.suspend_security()
        record = self.approve_document.suspend_security()
        if record and hasattr(record, 'urgency_id'):
            if record.urgency_id == 'general':
                result = '1'
            elif record.urgency_id == 'urgent':
                result = '0'
            else:
                result = '1'
        else:
            result = '1'
        return result


    @api.multi
    @api.depends('wkf_line_instance_id', 'from_task_id')
    def _compute_wkf_line_instance_name(self):
        for rec in self:
            if rec.from_task_id:
                approve_result = rec.from_task_id.with_context(prefetch_fields=False).approve_result_id
                approve_result_code = approve_result.code
                if approve_result_code in ['transfer', 'linkup', 'add']:
                    rec.wkf_line_instance_name = ' '
                else:
                    rec.wkf_line_instance_name = rec.wkf_line_instance_id.with_context(prefetch_fields=False).workflow_line_id.with_context(prefetch_fields=False).name
            else:
                rec.wkf_line_instance_name = rec.wkf_line_instance_id.with_context(prefetch_fields=False).workflow_line_id.with_context(prefetch_fields=False).name

    @api.multi
    @api.depends('approve_document', 'wkf_line_instance_id')
    def _compute_other_matter(self):
        for rec in self:
            try:
                result = ''
                if rec.approve_document and rec.wkf_line_instance_id:
                    approve_document = rec.approve_document.suspend_security()
                    ref_model = self.env['workflow.model'].search([('model_id', '=', rec.wkf_line_instance_id.instance_id.workflow_id.wkf_model_id.id)], limit=1)
                    if ref_model.other_key_ids:
                        for x in ref_model.other_key_ids:
                            field_name = x.field_id.name
                            if x.ttype == 'many2one':
                                x_relation_record = getattr(approve_document, field_name).suspend_security()
                                if x_relation_record:
                                    value = x_relation_record.name_get()[0][1]
                                else:
                                    value = ''
                            elif x.ttype == 'selection':
                                doc_model = self.env[approve_document._name]
                                field_cls = doc_model._fields.get(field_name)
                                description = field_cls.get_description(doc_model.env)
                                selections = description.get('selection', [])
                                value = getattr(approve_document, field_name)
                                for y in selections:
                                    if y[0] == value:
                                        value = y[1]
                                        break
                            else:
                                value = getattr(approve_document, field_name)
                            if value:
                                result += '[%s]:%s;' % (x.field_description, str(value))
            except Exception as e:
                result = ''
            if result:
                rec.other_key_matter = result

    @api.multi
    @api.depends('approve_document')
    def _compute_doc_state(self):
        for rec in self:
            state = False
            try:
                if rec.approve_document and hasattr(rec.approve_document, 'state'):
                    doc_model = self.env[rec.approve_document._name]
                    field_cls = doc_model._fields.get('state')
                    description = field_cls.get_description(doc_model.env)
                    selections = description.get('selection', [])
                    for x in selections:
                        if x[0] == rec.approve_document.state:
                            state = x[1]
                            break
            except Exception as e:
                pass
            rec.doc_state = state


    @api.multi
    def get_note(self):
        self.ensure_one()
        self = self.suspend_security()
        if self.note:
            result = _('Note:%s') % self.note
        elif self.approve_document and hasattr(self.approve_document, 'x_note'):
            result = _('Note:%s') % self.approve_document.x_note
        else:
            result = ''
        return result

    @api.multi
    def view_attachments_info(self):
        self.ensure_one()
        self = self.sudo()
        if not self.approve_attachments_ids:
            return
        tree_view = self.env.ref('workflow.view_workflow_ir_attachment_tree')
        return {
            'name': _("Attachments"),
            'type': 'ir.actions.act_window',
            'res_model': "ir.attachment",
            'view_type': 'tree',
            'views': [(tree_view.id, 'tree')],
            'view_mode': 'tree',
            'target': 'new',
            'domain': [('id', 'in', self.approve_attachments_ids.ids)],
            'context': "{'suspend_security':True,'login_name':True}"
        }

    @api.model
    def get_to_be_approved_task(self, employee, is_super=False, my_requests=False):
        if not is_super:
            domain = [('approve_result_id', '=', False),
                      ('wkf_line_instance_state', '=', 'processing')]
            if not my_requests:
                domain += [('allocated_approver_id', '=', employee.id)]
            else:
                domain += [('submit_employee_id', '=', employee.id)]
            tasks = self.search(domain, order='submit_time desc')
        else:
            domain = [('approve_result_id', '=', False),
                      ('wkf_line_instance_state', '=', 'processing')]
            if my_requests:
                domain += [('submit_employee_id', '=', employee.id)]
            tasks = self.search(domain, order='submit_time desc')
        if my_requests and tasks:
            instances = tasks.mapped('wkf_line_instance_id').mapped('instance_id')
            if instances:
                my_requests_tasks = self.env['workflow.task']
                for instance in instances:
                    s_task = self.search([('id', 'in', tasks.ids),
                                          ('wkf_line_instance_id.instance_id', '=', instance.id)], limit=1)
                    my_requests_tasks += s_task
                if my_requests_tasks:
                    tasks = my_requests_tasks
        return tasks

    # 此处为消息接口
    @api.model
    def get_approved_task(self, employee, my_requests=False,show_days=None, offset=0, limit=None, search_value=None):
        if not show_days:
            show_days=7
        # 将此处消息配置变为传递进来的值
        seven_ago = fields.Datetime.to_string(fields.Datetime.from_string(fields.Datetime.now()) - timedelta(days=show_days))
        employee_id = employee if isinstance(employee, int) else employee.id
        if employee:
            value_domain = []
            if search_value:
                value_domain = ['|', '|', '|', '|',
                                ('document_name', 'like', search_value),
                                ('submit_user_id.related_employee_id.name_related', 'like', search_value),
                                ('submit_time', 'like', search_value),
                                ('company_id.name', 'like', search_value),
                                ('note', 'like', search_value)]
        map_approve_setting_models = self.env['map.approve.setting'].search([('usage', '=', 'map')]).mapped('ref_model_id.model')
        domain = ['|', ('wkf_model_id.model', 'in', EXIST_WECHAT_MODEL + map_approve_setting_models), ('approve_document', 'like', 'x_%'),
                  ('approve_result_id', '!=', False), ('submit_time', '>', seven_ago)]
        if not my_requests:
            domain += [('allocated_approver_id', '=', employee_id), ('approve_result_id.code', 'not in', ('withdraw', 'submit'))]
        else:
            domain += [('submit_employee_id', '=', employee_id), ('approve_result_id.code', '=', 'submit')]
        all_tasks = self.search(domain + value_domain)
        sql = """
            select max(id) as id, max(approve_time) as approve_time from workflow_task 
            where id in {all_tasks_ids} and approve_time is not null 
            group by approve_document order by approve_time desc limit {limit} offset {offset}
        """.format(all_tasks_ids=tuple(all_tasks.ids),
                   limit=limit,
                   offset=offset)
        self.env.cr.execute(sql)
        results = self.env.cr.dictfetchall()
        tasks = self.browse(result.get("id") for result in results)
        # if my_requests and tasks:
        #     instances = tasks.mapped('wkf_line_instance_id').mapped('instance_id')
        #     if instances:
        #         my_requests_tasks = self.env['workflow.task']
        #         for instance in instances:
        #             s_task = self.search([('id', 'in', tasks.ids),
        #                                   ('wkf_line_instance_id.instance_id', '=', instance.id)], limit=1)
        #             my_requests_tasks += s_task
        #         if my_requests_tasks:
        #             tasks = my_requests_tasks
        return tasks

    @api.multi
    def task_info(self, employee=False):
        self.ensure_one()
        # flag = self.env.user.related_employee_id.id == self.allocated_approver_id.id
        if employee:
            flag = employee.id == self.allocated_approver_id.id
        else:
            flag = True
        if self.approve_result_id:
            opt = False
        else:
            opt = True
        opt = opt and flag
        has_transfer = self.approve_document.get_has_transfer(self)
        has_return = self.approve_document.get_has_return(self)
        has_add = self.approve_document.get_has_add(self)
        has_linkup = self.approve_document.get_has_linkup(self)
        vals = {'task_id': self.id,
                'model': self.approve_document._name,
                'id': self.approve_document.id,
                'db_name': self.env.cr.dbname,
                'company': self.company_id.name,
                'status': opt,
                'has_transfer': has_transfer,
                'has_return': has_return,
                'has_add': has_add,
                'has_linkup': has_linkup,
                }
        vals['approve_records'] = self.get_approve_records_info(self.wkf_line_instance_id.instance_id)
        vals.update(self.approve_document.document_info(self.id))
        vals['attachment'] = self.get_attachments()
        return vals

    @api.multi
    def task_info_list(self, opt):
        self.ensure_one()
        is_tongyong = True if self.approve_document._name.startswith('x_') else False
        customer = self.env['ir.config_parameter'].sudo().get_param('kthrp_customer_code', 'kthrp')
        map_approve_type = self.get_approve_document_type()
        setting = self.env.ref('workflow.workflow_default_setting', raise_if_not_found=False)
        vals = {'task_id': self.id,
                'model': self.approve_document._name,
                'id': self.approve_document.id,
                'db_name': self.env.cr.dbname,
                'submit_time': lang_datetime(self.env.user.lang, self.submit_time),
                'submitter': self.submit_user_id.name,
                'company': self.company_id.name,
                'status': opt,
                'note': self.note or '',
                'kthrp_customer_code': customer,
                'is_tongyong': is_tongyong,
                'map_approve_type': map_approve_type,
                'amount': self.doc_amount,
                'skip_approved_node': setting.default_of_skip_approved_node
                }
        vals.update(self.with_context(map_approve_type=map_approve_type,
                                      is_general_map=True).approve_document.document_info_list())
        return vals

    @api.multi
    def get_approve_document_type(self, approve_document=None):
        if not approve_document:
            self.ensure_one()
        approve_document = approve_document or self.approve_document
        possible_type_fields = ['type_id', 'order_type_id', 'requisition_type_id', 'requisition_purchase_type_id',
                                'stock_out_in_type_id', 'related_po_type_id', 'leave_type_id', 'batch_type_id',
                                'sourcing_type_id', 'plan_type_id', 'doc_type_id', 'document_type_id']
        for field in possible_type_fields:
            if hasattr(approve_document, field) and getattr(approve_document, field, False):
                return getattr(approve_document, field)._name + ',' + str(getattr(approve_document, field).id)
        else:
            return ''

    @api.multi
    def task_info_line(self, line_id, line_type=None):
        self.ensure_one()
        vals = {'line_id': line_id,
                'task_id': self.id}
        vals.update(self.approve_document.document_info_line(line_id, line_type=line_type))
        return vals

    @api.multi
    def get_attachments(self):
        self.ensure_one()
        urls = []
        if self.approve_document._name.startswith('x_'):
            attachments, general_setting = self.approve_document.get_attachments()
        else:
            attachments = self.approve_document.get_attachments()
            general_setting = False
        if not attachments:
            return []
        if self.approve_document._name == 'kthrp.contract.contract':
            base_url = self.env['ir.config_parameter'].get_web_base_url_out() + '/web/content'
            for rec in attachments:
                special_str = '?model=%s&field=attachment&id=%s&download=true&filename_field=attachment_name&database=%s&skip=1&download=true' % (
                rec._name, rec.id, self.env.cr.dbname)
                url = base_url + special_str
                bin_data = rec.attachment and rec.attachment.decode('base64') or ''
                urls.append({'url': url,'id':rec.id, 'name': rec.attachment_name, 'size': len(bin_data)})
        else:
            base_url = self.env['ir.config_parameter'].get_web_base_url_out()
            if len(base_url) >= 16:
                key = base_url[:16]
            else:
                key = (base_url + 'kkkkkkkkkkkkkkkk')[:16]
            aes_encrypt = prpcrypt(key)
            for rec in attachments:

                special_str = '?database=%s&mapskip=%s&download=true' % (self.env.cr.dbname, aes_encrypt.encrypt(str(rec.id) + '_map_file'))
                a_name = rec.datas_fname or u'附件'
                url = base_url + '/web/content/' + aes_encrypt.encrypt(str(rec.id)) + '/' + a_name + special_str
                urls.append({'url': url, 'name': a_name, 'size': rec.file_size,'id':rec.id,
                             'key': general_setting and general_setting.attachment_page_name or u'附件'})
        return urls

    @api.multi
    def get_task_attachments(self):
        self.ensure_one()
        result = []
        base_url = self.env['ir.config_parameter'].get_web_base_url_out()
        if len(base_url) >= 16:
            key = base_url[:16]
        else:
            key = (base_url + 'kkkkkkkkkkkkkkkk')[:16]
        aes_encrypt = prpcrypt(key)
        for rec in self.approve_attachments_ids:
            special_str = '?database=%s&mapskip=%s&download=true' % (self.env.cr.dbname, aes_encrypt.encrypt(str(rec.id) + '_map_file'))
            a_name = rec.datas_fname or u'附件'
            url = base_url + '/web/content/' + aes_encrypt.encrypt(str(rec.id)) + '/' + a_name + special_str
            result.append({'url': url, 'name': a_name})
        return result

    @api.multi
    def transfer_approve_attachments(self):
        self.ensure_one()
        self = self.suspend_security()
        approve_document = self.approve_document
        if approve_document._name == 'kthrp.contract.contract':
            contract_attachment_obj = self.env['kthrp.contract.contract.attachment']
            for rec in self.approve_attachments_ids:
                vals = {'no': len(approve_document.attachment_ids) + 1,
                        'attachment': rec.datas,
                        'attachment_name': rec.name,
                        'upload_time': self.approve_time,
                        'upload_by': self.actual_approver_id.id,
                        'note': self.wkf_line_instance_id.workflow_line_id.name,
                        'contract_id': approve_document.id
                        }
                contract_attachment_obj.create(vals)
        elif approve_document._name in ('kthrp.contract.contract.amendment', 'kthrp.contract.supplemental.agreement'):
            approve_document.attachment_ids = [(4, x) for x in self.approve_attachments_ids.ids]
        else:
            pass

    @api.multi
    def transfer_approve_attachments_x(self):
        pass

    @api.multi
    def _compute_current_line_instance(self):
        result_dict = {}
        sql = '''SELECT kbwt."id",
            ( SELECT skbwil."id"
            FROM workflow_instance_line skbwil
            WHERE skbwil.instance_id = kbwi."id"
            AND ((kbwi."state" = 'processing' AND skbwil."state" = 'processing')
            OR (kbwi."state" IN ('completed', 'rejected')))
            ORDER BY skbwil.id desc
            LIMIT 1 ) AS current_line_instance_id,
            kbwi."state" AS instance_approve_state
            FROM
                workflow_task kbwt,
                workflow_instance_line kbwil,
                workflow_line kbwl,
                workflow_instance kbwi
            WHERE
                kbwt.wkf_line_instance_id = kbwil."id"
            AND kbwil.instance_id = kbwi."id"
            AND kbwil.workflow_line_id = kbwl."id"
            AND kbwt.id in %s  '''
        self._cr.execute(sql, (tuple(self.ids), ))
        results = self._cr.dictfetchall()
        for result in results:
            result_dict[result.get('id')] = [result.get('current_line_instance_id'), result.get('instance_approve_state')]
        for rec in self:
            result_list = result_dict.get(rec.id)
            if result_list:
                rec.current_line_instance_id = result_list[0]
                rec.instance_approve_state = result_list[1]
        # for rec in self:
        #     instance_id = rec.wkf_line_instance_id.instance_id
        #     if instance_id.state == 'processing':
        #         rec.current_line_instance_id = instance_id.instance_line_ids.filtered(
        #             lambda line: line.state == 'processing').id
        #     else:
        #         end_line = instance_id.instance_line_ids.filtered(
        #             lambda line: line.workflow_line_id.node_type == 'end')
        #         if end_line:
        #             rec.current_line_instance_id = end_line[0].id
        #         else:
        #             res = self.env['workflow.instance.line'].search(
        #                 [('id', 'in', instance_id.instance_line_ids.ids)],
        #                 order='process_time desc', limit=1)
        #             if res:
        #                 rec.current_line_instance_id = res.id

    @api.model
    def get_approve_records_info(self, instance_id):
        lines = []
        model_id = self.env['ir.model'].search([('model', '=', instance_id.approve_document._name)], limit=1)
        if model_id:
            tasks = self.env['workflow.task'].search([('wkf_model_id', '=', model_id.id),
                                                                 ('model_instance_id', '=',
                                                                  instance_id.approve_document.id)],
                                                                order='id, create_date')
            withdraw_instances = []
            for task in tasks:
                if task.approve_result_id.code == 'withdraw' and task.wkf_line_instance_id.instance_id.id in withdraw_instances:
                    continue
                if task.approve_result_id.code == 'withdraw':
                    withdraw_instances.append(task.wkf_line_instance_id.instance_id.id)
                approve_time = lang_datetime(self.env.user.lang, task.approve_time or fields.Datetime.now())
                approve_time_datetime = fields.Datetime.from_string(approve_time)
                if task.approve_attachments_ids:
                    file_info = task.get_task_attachments()
                else:
                    file_info = []
                result = ''
                if task.approve_result_id.name:
                    result = task.approve_result_id.name
                elif task.wkf_line_instance_id.state == 'processing' and not task.approve_result_id.name:
                    result = u'审批中'
                else:
                    pass

                line_val = {'month': str(approve_time_datetime.month) + u'月',
                            'day': str(approve_time_datetime.day) + u'日',
                            'time': approve_time[-8:],
                            'approver': task.actual_approver_id.name or task.allocated_approver_id.name or '',
                            'result': result,
                            'note': task.approve_note or '',
                            'file_info': file_info,
                            }
                lines.append(line_val)
        return lines

    @api.multi
    def get_next_node(self, document_id=False, document_model=False):
        self.ensure_one()
        self = self.suspend_security()
        if not document_model:
            document_model = self.approve_document._name
        if not document_id:
            document_id = self.approve_document.id
        transitions = self.wkf_line_instance_id.workflow_line_id.out_transition_ids.filtered(
            lambda t: t.to_workflow_line_id.node_type == "approve")
        transitions = sorted(transitions, key=lambda x: x.sequence)
        next_node = None
        for rec in transitions:
            if not rec.wkf_condition_id:
                next_node = rec.to_workflow_line_id
                break
            else:
                if rec.wkf_condition_id.check_match_domain(document_model, document_id) is True:
                    next_node = rec.to_workflow_line_id
                    break
                else:
                    pass
        if next_node and len(next_node) != 1:
            return False
        elif not next_node:
            return False
        return next_node

    # 提供工作流节点流转上的通知功能
    @api.multi
    def transition_notify(self):
        self.ensure_one()
        self = self.suspend_security()
        role_obj = self.env['workflow.role']
        wkf_instance = self.wkf_line_instance_id.instance_id
        document_model = self.wkf_line_instance_id.instance_id.approve_document._name
        document_id = self.wkf_line_instance_id.instance_id.approve_document.id
        transitions = self.wkf_line_instance_id.workflow_line_id.out_transition_ids
        transitions = sorted(transitions, key=lambda x: x.sequence)
        transition = None
        for rec in transitions:
            if not rec.wkf_condition_id:
                transition = rec
                break
            else:
                if rec.wkf_condition_id.check_match_domain(document_model, document_id) is True:
                    transition = rec
                    break
                else:
                    pass
        if transition:
            if transition.notify:
                if self.wkf_line_instance_id.workflow_line_id.is_start:
                    notify_type = 'submit_notify'
                else:
                    notify_type = 'notify'
                if transition.notify_type == 'designated_person':
                    notify_ids = self.env['hr.employee'].browse(transition.notify.id)
                    self.with_context(
                        {'partner_ids': notify_ids, 'lang': self.env.context.get('lang', 'en_US')})._wkf_send_email(
                        notify_type)
                    self.update({'notifier': notify_ids})
                elif transition.notify_type == 'approve_role':
                    role = role_obj.browse(transition.notify.id)
                    notify_ids = role_obj.get_role_approver_direct(wkf_instance, role)
                    self.with_context(
                        {'partner_ids': notify_ids, 'lang': self.env.context.get('lang', 'en_US')})._wkf_send_email(
                        notify_type)
                    self.update({'notifier': notify_ids})

    @api.multi
    def _compute_has_signal(self):
        for rec in self:
            rec.has_signal = rec.approve_result_id.id
            if self.env.user.id == SUPERUSER_ID:
                rec.is_admin = True
            else:
                rec.is_admin = False

    @api.multi
    @api.depends('wkf_line_instance_id')
    def _compute_submit_info(self):
        for rec in self:
            if rec.wkf_line_instance_id.instance_id.approve_document:
                rec.submit_user_id = rec.wkf_line_instance_id.instance_id.approve_document.submit_user_id.id
                rec.submit_time = rec.wkf_line_instance_id.instance_id.approve_document.submit_time

    @api.multi
    @api.depends('wkf_line_instance_id')
    def _compute_document_model(self):
        for rec in self:
            if rec.wkf_line_instance_id:
                rec.document_model_id = rec.wkf_line_instance_id.workflow_line_id.workflow_id.wkf_model_id.id

    @api.multi
    @api.depends('wkf_line_instance_id')
    def _compute_doc_amount(self):
        for rec in self:
            if rec.wkf_line_instance_id:
                try:
                    approve_document = rec.approve_document
                except Exception as e:
                    approve_document = False
                if approve_document:
                    approve_document = approve_document.suspend_security().with_context(prefetch_fields=False)
                    if approve_document._name == 'kthrp.payable.invoice':
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.unpaid_amount)
                    elif approve_document._name == 'kthrp.receivable.receipt.batch':
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.total_receipt_amount)
                    elif approve_document._name == 'kthrp.contract.receipt.application':
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.total_process_amount)
                    elif approve_document._name == 'kthrp.payroll.salary.summary.payslip':
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.total_net_salary)
                    elif approve_document._name == 'kthrp.receivable.receivable.batch':
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.total_invoice_amount)
                    elif approve_document._name == 'kthrp.stock.inventory':
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.total_difference_amount)
                    elif approve_document._name == 'kthrp.payroll.salary.adjustment.application':
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.new_salary)
                    elif approve_document._name == 'kthrp.cost.cost.difference.todo':
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.different_amount)
                    elif approve_document._name == 'kthrp.budget.asset.budget.application':
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.budget_amount)
                    elif approve_document._name == 'kthrp.expense.borrowing':
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.borrow_amount)
                    elif approve_document._name == 'kthrp.contract.contract.amendment':
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.amendment_amount)
                    elif approve_document._name == 'kthrp.contract.contract':
                        rec.doc_amount = decimal.Decimal("%.2f" % (approve_document.total_amount or approve_document.agreed_amount))
                    elif approve_document._name == 'kthrp.fund.bank.corporate.payment.batch':
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.payment_total_amount)
                    elif approve_document._name == 'kthrp.expense.repayment':
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.total_repayment_amount)
                    elif approve_document._name == 'kthrp.sales.receivable.sales.invoice':
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.amount)
                    elif approve_document._name == 'kthrp.purchase.return.order':
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.total_return_amount)
                    elif approve_document._name == 'kthrp.hr.reception.application':
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.total_fee)
                    elif approve_document._name == 'kthrp.research.fund.arrival.application':
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.arrival_amount)
                    elif approve_document._name in (
                    'kthrp.expense.expense.requisition', 'kthrp.payable.payment.request'):
                        rec.doc_amount = decimal.Decimal("%.2f" % approve_document.requisition_amount)
                    else:
                        try:
                            if hasattr(approve_document, 'total_amount'):
                                rec.doc_amount = decimal.Decimal("%.2f" % approve_document.total_amount)
                            else:
                                if hasattr(approve_document, 'x_total_amount'):
                                    rec.doc_amount = decimal.Decimal("%.2f" % approve_document.x_total_amount)
                                else:
                                    rec.doc_amount = ''
                        except:
                            rec.doc_amount = ''

    @api.multi
    @api.depends('wkf_line_instance_id')
    def _compute_document_company(self):
        for rec in self:
            try:
                if rec.wkf_line_instance_id:
                    approve_document = rec.wkf_line_instance_id.instance_id.approve_document
                    if approve_document:
                        approve_document = approve_document.suspend_security()
                        if approve_document._name in (
                        'kthrp.hr.employee.change.apply', 'kthrp.hr.employee.change.resign',
                        'kthrp.hr.leave.record', 'kthrp.payroll.salary.adjustment.application',
                        'kthrp.payroll.salary.summary.payslip', 'kthrp.hr.overtime.application',
                        'kthrp.hr.bank.account.change', 'kthrp.hr.resumption.leave.application',
                        'kthrp.hr.outwork.application', 'kthrp.hr.overtime.application',
                        'kthrp.hr.bank.account.change', 'kthrp.hr.recruitment.application',
                        'kthrp.hr.employee.change.further.study',
                        'kthrp.hr.employee.change.apply',
                        'kthrp.employee.information.report',
                        'kthrp.hr.contract.renewal.application',
                        'kthrp.hr.employment.contract',
                        'kthrp.hr.timesheet',
                        'kthrp.hr.employee.change.global.transfer',
                        'kthrp.hr.recruitment.application',
                        'kthrp.payroll.si.result',
                        'kthrp.payroll.special.salary.fee.application',
                        'kthrp.payroll.bonus.matter.application',
                        'kthrp.hr.chanyi.employee.hpf.purchase.application',
                        'kthrp.hr.recruitment.plan',
                        ):
                            try:
                                rec.company_id = approve_document.legal_company_id.id
                            except Exception as e:
                                pass
                        else:
                            try:
                                if hasattr(approve_document, 'company_id'):
                                    rec.company_id = approve_document.company_id.id
                                elif hasattr(approve_document, 'x_company_id'):
                                    rec.company_id = approve_document.x_company_id.id
                                elif hasattr(approve_document, 'x_company'):
                                    rec.company_id = approve_document.x_company.id
                                else:
                                    rec.company_id = False
                            except Exception as e:
                                pass
            except Exception as e:
                pass

    @api.model
    def get_document_company(self, approve_document):
        company_id = False
        if not approve_document:
            return company_id
        try:
            if approve_document:
                approve_document = approve_document.suspend_security()
                if approve_document._name in ('kthrp.hr.employee.change.apply', 'kthrp.hr.employee.change.resign',
                                              'kthrp.hr.leave.record',
                                              'kthrp.payroll.salary.adjustment.application',
                                              'kthrp.payroll.salary.summary.payslip',
                                              'kthrp.hr.overtime.application',
                                              'kthrp.hr.bank.account.change',
                                              'kthrp.hr.resumption.leave.application',
                                              'kthrp.hr.outwork.application', 'kthrp.hr.overtime.application',
                                              'kthrp.hr.bank.account.change', 'kthrp.hr.recruitment.application',
                                              'kthrp.hr.employee.change.further.study',
                                              'kthrp.hr.employee.change.apply',
                                              'kthrp.employee.information.report',
                                              'kthrp.hr.contract.renewal.application',
                                              'kthrp.hr.employment.contract',
                                              'kthrp.hr.timesheet',
                                              'kthrp.hr.employee.change.global.transfer',
                                              'kthrp.hr.recruitment.application',
                                              'kthrp.payroll.si.result',
                                              'kthrp.payroll.special.salary.fee.application',
                                              'kthrp.payroll.bonus.matter.application',
                                              'kthrp.hr.chanyi.employee.hpf.purchase.application'
                                              ):
                    try:
                        company_id = approve_document.legal_company_id.id
                    except Exception as e:
                        pass
                else:
                    try:
                        if hasattr(approve_document, 'company_id'):
                            company_id = approve_document.company_id.id
                        elif hasattr(approve_document, 'x_company_id'):
                            company_id = approve_document.x_company_id.id
                        elif hasattr(approve_document, 'x_company'):
                            company_id = approve_document.x_company.id
                        else:
                            company_id = False
                    except Exception as e:
                        pass
        except Exception as e:
            pass
        return company_id

    @api.model
    def get_document_note(self, approve_document):
        note = ''
        if not approve_document:
            return note
        try:
            if approve_document and approve_document._name in self.env.registry.models:
                try:
                    if hasattr(approve_document, 'note'):
                        note = approve_document.note
                    elif hasattr(approve_document, 'x_note'):
                        note = approve_document.x_note
                    else:
                        note = ''
                except Exception as e:
                    note = ''
        except Exception as e:
            pass
        return note

    @api.multi
    def compute_last_wkf_line_instance_id(self):
        for rec in self:
            last_node = self.env['workflow.instance.line.transition'].search(
                [('to_instance_line_id', '=', rec.wkf_line_instance_id.id)], limit=1)
            if last_node:
                rec.last_wkf_line_instance_id = last_node.from_instance_line_id.id
                rec.last_approve_time = last_node.from_instance_line_id.process_time

    @api.multi
    def get_special_transfer_and_task(self):
        self.ensure_one()
        is_special_transfer = self.is_special_transfer
        from_task_id = self.from_task_id
        task = from_task_id
        if not is_special_transfer:
            while task:
                if task.is_special_transfer:
                    is_special_transfer = True
                    from_task_id = task.from_task_id
                    break
                task = task.from_task_id
        return is_special_transfer, from_task_id

    # 系统工作流消息置为已读
    def set_workflow_message_done(self, approve_document, detail_class=None):
        domain = [('res_id', '=', approve_document.id), ('model', '=', approve_document._name)]
        if detail_class:
            domain += [('detail_class', '=', detail_class)]
        message_ids = self.env['mail.message'].search(domain)
        if message_ids:
            message_ids.set_message_done()

    @api.multi
    def action_approve(self, not_notify=False):
        self = self.suspend_security()
        self.ensure_one()
        # 判断是否重复审批
        if self.approve_document:
            self.approve_document.action_approve_check()
        if self.approve_result_id or self.wkf_line_instance_state != 'processing':
            raise ValidationError(
                _('Error:Cannot approve this approved task!'))
        self.wkf_line_instance_id.instance_id.process('approve', self)
        if not not_notify:
            is_special_transfer, from_task_id = self.get_special_transfer_and_task()
            if not self.wkf_line_instance_id.is_end and (not self.is_finally_approve or is_special_transfer):
                self._wkf_send_email('been_approved')
            elif self.is_finally_approve:
                pass
                # self._wkf_send_email('approved')
        is_special_transfer, from_task_id = self.get_special_transfer_and_task()
        if from_task_id.approve_result_id.code in ('add', 'linkup'):
            if from_task_id.approve_result_id.code == 'linkup':
                all_other_linkup_tasks = self.env['workflow.task'].search([('from_task_id', '=', from_task_id.id),
                                                                                      ('id', '!=', self.id),
                                                                                      ('approve_result_id', '=', False)])
                if all_other_linkup_tasks:
                    pass
                else:
                    self.return_approve(from_task_id)
                reply_signal = self.env['workflow.signal'].search([('code', '=', 'reply')])
                self.approve_result_id = reply_signal.id
            elif from_task_id.approve_result_id.code == 'add':
                all_other_add_tasks = self.env['workflow.task'].search([('from_task_id', '=', from_task_id.id),
                                                                                   ('id', '!=', self.id),
                                                                                   ('approve_result_id', '=', False)])
                if all_other_add_tasks:
                    pass
                else:
                    if is_special_transfer:
                        self.return_approve(from_task_id)
                    else:
                        self.process_repeat_approve()

        else:
            if is_special_transfer:
                self.return_approve(from_task_id)
            else:
                self.process_repeat_approve()

        # 更新 oa 消息状态
        if self.has_install_module('kthrp_dingding'):
            ding_config_obj = self.env['kthrp.dingding.config']
            # 直发直接更新 oa 消息状态
            if ding_config_obj.search([], limit=1).send_type == 'hrp_direct_send':
                # 更新钉钉 oa 消息状态
                if self.dingding_msg_oa:
                    Dingtalk = dingding_interface.KthrpDingtalk
                    oa_task_id, ding_config_id = self.dingding_msg_oa.split('-')[0], \
                                                 self.dingding_msg_oa.split('-')[1]
                    ding_config_obj = self.env['kthrp.dingding.config']
                    corpid, corpsecret, agent_id, token_dict = ding_config_obj.browse(
                        int(ding_config_id)).get_ding_common_message()
                    ding_obj = Dingtalk(corpid, corpsecret, agent_id, token_dict)
                    ding_obj.update_oa_msg_state(int(oa_task_id))

                    # 同一节点多个人审批，一人通过，则将其他消息状态同步更新
                    if len(self.wkf_line_instance_id.task_ids) > 1:
                        if self.wkf_line_instance_state == 'processed':
                            for tk in self.wkf_line_instance_id.task_ids:
                                if tk.dingding_msg_oa:
                                    oa_task_id, ding_config_id = tk.dingding_msg_oa.split('-')[0], \
                                                                 tk.dingding_msg_oa.split('-')[1]
                                    ding_config_obj = self.env['kthrp.dingding.config']
                                    corpid, corpsecret, agent_id, token_dict = ding_config_obj.browse(
                                        int(ding_config_id)).get_ding_common_message()
                                    ding_obj = Dingtalk(corpid, corpsecret, agent_id, token_dict)
                                    ding_obj.update_oa_msg_state(int(oa_task_id))
            # 前置机发送需要找到对应消息的 后台消息发送列表中的 update_oa_flag 值
            else:
                update_task_ids = [self.id, -1]
                # 同一节点多个人审批，一人通过，则将其他消息状态同步更新
                if len(self.wkf_line_instance_id.task_ids) > 1:
                    if self.wkf_line_instance_state == 'processed':
                        update_task_ids += self.wkf_line_instance_id.task_ids.ids
                update_sql = "update kthrp_dingding_msg_to_be_sent set update_oa_flag = 't' where workflow_task_id in {};".format(
                    tuple(set(update_task_ids)))

                self._cr.execute(update_sql)

    @api.multi
    def process_repeat_approve(self):
        self.ensure_one()
        document_model = self.approve_document._name
        document_id = self.approve_document.id
        task_obj = self.env['workflow.task']
        next_tasks = task_obj.search(
            [('approve_result_id', '=', False), ('wkf_line_instance_state', '=', 'processing'),
             ('wkf_line_instance_id.instance_id.approve_document', '=', document_model + ',' + str(document_id))])
        actual_approver_id = self.actual_approver_id.id
        current_workflow_line = self.wkf_line_instance_id.workflow_line_id
        for next_task in next_tasks:
            if not self.wkf_line_instance_id.out_transition_ids.filtered(lambda x : x.to_instance_line_id == next_task.wkf_line_instance_id):
                continue
            workflow_line = next_task.wkf_line_instance_id.workflow_line_id
            repeat_approve = workflow_line.repeat_approve
            allocated_approver_id = next_task.allocated_approver_id.id
            if repeat_approve or allocated_approver_id != actual_approver_id or workflow_line.approver_type == 'designated_person' or current_workflow_line.related_line_id:
                continue
            else:
                next_task.write({'approve_note': _('Adjacent approval nodes, skip automatically.'), 'auto_approve':True})
                next_task.with_context({'lang': self.env.context.get('lang', 'en_US')}).action_approve(not_notify=True)
                if not next_task.wkf_line_instance_id.is_end and not next_task.is_finally_approve:
                    pass
                elif next_task.is_finally_approve:
                    # next_task._wkf_send_email('approved')
                    pass
                next_task.transition_notify()

    @api.multi
    def return_approve(self, from_task_id):
        self = self.suspend_security()
        self.ensure_one()
        task_obj = self.env['workflow.task']
        vals = {'wkf_line_instance_id': self.wkf_line_instance_id.id,
                'allocated_approver_id': from_task_id.allocated_approver_id.id}
        if from_task_id.from_task_id:
            vals['from_task_id'] = from_task_id.from_task_id.id
            vals['is_special_transfer'] = from_task_id.is_special_transfer
        return_task = task_obj.create(vals)
        return_task._wkf_send_email('need_approval')
        return return_task

    @api.multi
    def return_before(self):
        self.ensure_one()
        self.write({
            'actual_approver_id': False,
            'signature_image':False,
            'approve_result_id': False,
            'approve_note': False,
            'approve_time': False,
        })

    @api.multi
    @api.depends('allocated_approver_id')
    def _compute_allocated_user_id(self):
        for rec in self:
            if rec.allocated_approver_id:
                allocated_user = self.env['res.users'].search(
                    [('related_employee_id', '=', rec.allocated_approver_id.id)],
                    limit=1)
                if allocated_user:
                    rec.allocated_user_id = allocated_user.id

    @api.multi
    @api.depends('actual_approver_id')
    def _compute_actual_user_id(self):
        for rec in self:
            if rec.actual_approver_id:
                actual_user = self.env['res.users'].search([('related_employee_id', '=', rec.actual_approver_id.id)],
                                                           limit=1)
                if actual_user:
                    rec.actual_user_id = actual_user.id

    @api.model
    def create(self, vals):
        res = super(WorkflowTask, self).create(vals)
        if res.wkf_line_instance_id.is_start:
            res.sudo()._wkf_send_email('submitted')
        if res.wkf_line_instance_id.workflow_line_id.in_transition_ids:
            if res.wkf_line_instance_id.is_end:
                res.sudo()._wkf_send_email('approved')
                # if res.wkf_line_instance_id.workflow_line_id.in_transition_ids[0].from_workflow_line_id.is_start and \
                #         res.wkf_line_instance_id.is_end:
                #     res.sudo()._wkf_send_email('approved')
        return res

    @api.multi
    @api.depends('wkf_line_instance_id.instance_id.approve_document')
    def _compute_document_name(self):
        lang = self.env.user.lang
        for rec in self:
            try:
                if rec.wkf_line_instance_id.instance_id.approve_document and rec.wkf_line_instance_id.instance_id.approve_document._name in self.env.registry.models:
                    approve_document_model = rec.approve_document._name
                    if approve_document_model == 'kthrp.hr.leave.record':
                        document_type = '休假申请' if lang == 'zh_CN' else 'Leave App'
                    elif approve_document_model == 'kthrp.hr.resumption.leave.application':
                        document_type = '销假申请' if lang == 'zh_CN' else 'Resumption Leave App'
                    elif approve_document_model == 'kthrp.hr.outwork.application':
                        document_type = '外出申请' if lang == 'zh_CN' else 'Outwork App'
                    elif approve_document_model == 'kthrp.hr.employee.change.apply':
                        if rec.approve_document.type_id.name:
                            document_type = str(rec.approve_document.type_id.name)
                        else:
                            document_type = ''
                    elif approve_document_model == 'kthrp.hr.employee.change.resign':
                        document_type = '离职申请' if lang == 'zh_CN' else 'Resign App'
                    elif approve_document_model == 'kthrp.hr.employee.change.global.transfer':
                        document_type = '跨公司调动' if lang == 'zh_CN' else 'Global Transfer'
                    elif approve_document_model == 'kthrp.hr.chanyi.employee.entry.application':
                        document_type = '入职申请' if lang == 'zh_CN' else 'Employee Entry Application'
                    else:
                        document_type = ''
                    rec.document_name = (rec.wkf_line_instance_id.instance_id.approve_document.with_context(
                        for_task=True).name_get()[0][1] or 'N/A') + document_type
            except Exception as e:
                pass

    @api.model
    def task_sql_create(self, vals, node_type=''):
        if not vals:
            return False
        params = []
        fields_str = ''
        # 提供特殊或个性化逻辑接口方法
        special_vals = self.get_special_value(vals.get('approve_document'))
        if special_vals:
            vals.update(special_vals)
        for k, v in vals.items():
            if fields_str:
                fields_str += ','
            fields_str += k
            params.append(v or None)
        fields_str += ',create_uid,write_uid,create_date,write_date'
        params += [self.env.user.id, self.env.user.id, fields.Datetime.now(), fields.Datetime.now()]
        params_str = '%s,' * len(params)
        params_str = params_str[:-1]
        sql = 'INSERT INTO workflow_task (%s) VALUES (%s) RETURNING id' % (fields_str, params_str)
        try:
            self.env.cr.execute(sql, params)
            task_id = self.env.cr.fetchall()[0][0]
        except Exception as e:
            task_id = False
        task = self.browse(task_id)
        if task_id and node_type=='end':
            task.sudo()._wkf_send_email('approved')
        elif task_id and node_type=='start':
            task.sudo()._wkf_send_email('submitted')
        task._compute_doc_amount()
        if hasattr(task, 'name'):
            task._compute_name()
        task._compute_wkf_line_instance_name()
        task._compute_department_id()
        return task_id

    # @api.multi
    # def get_special_value(self, approve_document):
    #     return {}

    @api.model
    def get_document_name(self, approve_document):
        result = ''
        if not approve_document:
            return result
        lang = self.env.user.lang
        try:
            if approve_document and approve_document._name in self.env.registry.models:
                approve_document_model = approve_document._name
                if approve_document_model == 'kthrp.hr.leave.record':
                    document_type = '休假申请' if lang == 'zh_CN' else 'Leave App'
                elif approve_document_model == 'kthrp.hr.resumption.leave.application':
                    document_type = '销假申请' if lang == 'zh_CN' else 'Resumption Leave App'
                elif approve_document_model == 'kthrp.hr.outwork.application':
                    document_type = '外出申请' if lang == 'zh_CN' else 'Outwork App'
                elif approve_document_model == 'kthrp.hr.employee.change.apply':
                    if approve_document.type_id.name:
                        document_type = str(approve_document.type_id.name)
                    else:
                        document_type = ''
                elif approve_document_model == 'kthrp.hr.employee.change.resign':
                    document_type = '离职申请' if lang == 'zh_CN' else 'Resign App'
                elif approve_document_model == 'kthrp.hr.employee.change.global.transfer':
                    document_type = '跨公司调动' if lang == 'zh_CN' else 'Global Transfer'
                elif approve_document_model == 'kthrp.hr.chanyi.employee.entry.application':
                    document_type = '入职申请' if lang == 'zh_CN' else 'Employee Entry Application'
                else:
                    document_type = ''
                result = (approve_document.with_context(
                    for_task=True).name_get()[0][1] or 'N/A') + document_type
        except Exception as e:
            pass
        return result

    @api.multi
    @api.depends('wkf_line_instance_id.instance_id.approve_document')
    def _compute_note(self):
        for rec in self:
            try:
                if rec.wkf_line_instance_id.instance_id.approve_document and rec.wkf_line_instance_id.instance_id.approve_document._name in self.env.registry.models:
                    try:
                        if hasattr(rec.wkf_line_instance_id.instance_id.approve_document, 'note'):
                            rec.note = rec.wkf_line_instance_id.instance_id.approve_document.note
                        elif hasattr(rec.wkf_line_instance_id.instance_id.approve_document, 'x_note'):
                            rec.note = rec.wkf_line_instance_id.instance_id.approve_document.x_note
                        else:
                            rec.note = ''
                    except Exception as e:
                        rec.note = ''
            except Exception as e:
                pass

    @api.multi
    def open_approve_document(self):
        self.ensure_one()
        self = self.suspend_security()
        ctx = self.env.context.copy()

        if self.approve_document:
            if self.approve_document._name in ['kthrp.expense.report', 'kthrp.contract.contract',
                                               'kthrp.payable.payment.request']:
                ctx.update({
                    # 'no_create': True,
                    # 'no_edit': True,
                    'no_delete': True,
                    'suspend_security': True,
                })
            else:
                ctx.update({
                    # 'no_create': True,
                    # 'no_edit': True,
                    'no_delete': True,
                    # 'no_action': True,
                    'suspend_security': True,
                })
            ctx.update({'active_model': self.approve_document._name, 'active_ids': self.approve_document.ids,
                        'active_id': self.approve_document.id})
            return self.with_context(ctx).approve_document.open_document()
        else:
            return False

    def signal(self, signal):
        self = self.with_context(prefetch_fields=False)
        if signal.code == 'approve' and self.wkf_line_instance_id.with_context(
                prefetch_fields=False).workflow_line_id.with_context(prefetch_fields=False).is_check_node:
            approve_result_id = self.env.ref('workflow.workflow_signal_check', raise_if_not_found=False).id
        else:
            approve_result_id = signal.id
        # self.write({'approve_result_id': approve_result_id, 'approve_time':fields.Datetime.now(), 'actual_approver_id': self.env.user.related_employee_id.id})
        if not self.skip_exception and not self.auto_approve:
            update_sql = 'UPDATE workflow_task SET approve_result_id=%s,approve_time=%s,actual_approver_id=%s, actual_user_id=%s, write_uid=%s, write_date=%s WHERE id =%s'
            params = [approve_result_id or None, fields.Datetime.now(), self.env.user.related_employee_id.id,
                      self.env.user.id, self.env.user.id, fields.Datetime.now(), self.id]
        else:
            update_sql = 'UPDATE workflow_task SET approve_result_id=%s,approve_time=%s,actual_approver_id=%s, actual_user_id=%s, write_uid=%s, write_date=%s WHERE id =%s'
            params = [approve_result_id or None, fields.Datetime.now(), self.allocated_approver_id.id or self.env.user.related_employee_id.id,
                      self.allocated_user_id.id or self.env.user.id, self.env.user.id, fields.Datetime.now(), self.id]
        if self.env.user.related_employee_id.with_context(prefetch_fields=False).signature_image:
            self.signature_image = self.env.user.related_employee_id.with_context(prefetch_fields=False).signature_image
        self.env.cr.execute(update_sql, params)
        # 暂时解决后面获取缓存失败问题。
        self.invalidate_cache()
        # self.approve_time = fields.Datetime.now()
        # self.actual_approver_id = self.env.user.related_employee_id.id

    @api.multi
    def action_refuse(self):
        # 判断是否重复审批
        if self.approve_result_id or self.wkf_line_instance_state != 'processing':
            raise ValidationError(
                _('Error:Cannot approve this approved task!'))
        self.wkf_line_instance_id.instance_id.process('reject', self)

    # task设置为撤回状态
    @api.one
    def callback(self):
        signal_obj = self.env["workflow.signal"]
        withdraw_signal = signal_obj.search([('code', '=', 'withdraw')], limit=1)
        value = {
            'approve_time': fields.Datetime.now(),
            'actual_approver_id': self.env.user.related_employee_id.id,
            'approve_result_id': withdraw_signal.id,
            'wkf_line_instance_name': '',
            'allocated_approver_id': '',
            'approve_note': _('Submitter Withdraw.')
        }
        self.process_value(value)
        self.write(value)

    def process_value(self, value):
        pass

    @api.multi
    def action_approve_wizard(self):
        view = self.env.ref('workflow.view_workflow_approve_form')
        return {
            'name': _('Approve.'),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.approve',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
            'context': {'_is_task': True}
        }

    @api.multi
    def action_reject_wizard(self):
        view = self.env.ref('workflow.view_workflow_reject_form')
        return {
            'name': _('Reject.'),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.reject',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
            'context': {'_is_task': True}
        }

    @api.multi
    def action_transfer_wizard(self):
        view = self.env.ref('workflow.view_workflow_transfer_form')
        lang = self.env.context.get('lang', '')
        return {
            'name': _t(lang, ['Transfer', u'转办']),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.transfer',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
            'context': {'_is_task': True}
        }

    @api.multi
    def action_linkup_wizard(self):
        view = self.env.ref('workflow.view_workflow_linkup_form')
        lang = self.env.context.get('lang', '')
        return {
            'name': _t(lang, ['Linkup', u'沟通']),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.linkup',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
            'context': {'_is_task': True}
        }

    @api.multi
    def action_add_wizard(self):
        view = self.env.ref('workflow.view_workflow_add_form')
        lang = self.env.context.get('lang', '')
        return {
            'name': _t(lang, ['Add', u'加签']),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.add',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
            'context': {'_is_task': True}
        }

    @api.multi
    def action_return_wizard(self):
        view = self.env.ref('workflow.view_workflow_return_form')
        return {
            'name': _('Return.'),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.return',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
            'context': {'_is_task': True}
        }

    @api.multi
    def action_intervene_wizard(self):
        view = self.env.ref('workflow.view_workflow_intervene_form')
        return {
            'name': _('Intervene.'),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.intervene',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new'
        }

    @api.model
    def get_user(self, employee_ids):
        return self.env['res.users'].search([('related_employee_id', 'in', employee_ids)])

    @api.multi
    def get_doc_url(self):
        self.ensure_one()
        self = self.suspend_security()
        # home_menu_id = self.env.ref('kthrp_portal.menu_kthrp_portal_root', raise_if_not_found=False)
        # home_action_id = self.env.ref('kthrp_portal.action_homepage', raise_if_not_found=False)
        # if home_menu_id and home_action_id:
        #     base_url = self.env['ir.config_parameter'].get_param(
        #             'web.base.url')
        #     special_str = '/web?menu_id=%s&action=%s' % (str(home_menu_id.id), str(home_action_id.id))
        #     result = base_url + special_str
        setting = self.env.ref('workflow.workflow_default_setting', raise_if_not_found=False)
        if self.approve_document:
            if setting.external_links_clickable:
                result = self.env['ir.config_parameter'].get_param(
                    'web.base.url') + '/web?#view_type=form&model=' + self.approve_document._name + '&id=' + str(
                    self.approve_document.id)
            else:
                result = '#'

        else:
            result = '#'
        return result

    @api.multi
    def check_linkup_or_add_processed(self):
        self = self.suspend_security()
        if not self.from_task_id:
            return True
        all_other_tasks = self.env['workflow.task'].search([('from_task_id', '=', self.from_task_id.id),
                                                                       ('id', '!=', self.id),
                                                                       ('approve_result_id', '=', False)])
        if not all_other_tasks:
            return True
        return False


    # 邮件发送后台化
    @api.multi
    def _wkf_send_email(self, mail_type):
        self.env['ir.config_parameter']._check_database_uuid()
        setting = self.env.ref('workflow.workflow_default_setting')
        if not setting.delay_send_email:
            self._wkf_send_email_job(mail_type, self.env.context)
        else:
            delay_minutes = setting.delay_minutes or 1
            task_timestamp = int(time.time()) + delay_minutes * 60
            sql = '''INSERT INTO workflow_email_queue(task_id,mail_type,document_name,timestamp,context,state,create_date,write_date,create_uid,write_uid)
                     VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'''
            for rec in self:
                params = [rec.id, mail_type, rec.document_name or None, task_timestamp,str(self.env.context), 'not_send',
                          fields.Datetime.now(), fields.Datetime.now(), 1, 1]
                self.env.cr.execute(sql, params)

    @job
    @api.multi
    def _wkf_send_email_job(self, mail_type, context):
        self = self.with_context(dict(context))
        self = self.with_context(lang=self.env.user.lang)
        workflow_message = {'submitted': _('Application %s has been submitted!'),
                            'need_approval': _('Application %s submitted by %s need your approval!'),
                            'need_approval_transfer': _(
                                'Application %s submit by %s, transfer from %s, need your approval!'),
                            'need_approval_consult': _(
                                'Application %s submit by %s, %s ask for your advice, please handle it!'),
                            'withdrawn': _('Application %s has been withdrawn!'),
                            'approved': _('Your application %s has been approved!'),
                            'been_approved': _('Your application %s has been approved by %s!'),
                            'notify': _('Submitted by %s application %s has been processed by %s!'),
                            'submit_notify': _('Application %s is submitted, please note!'),
                            'rejected': _('Your application %s has been rejected by %s!\n'
                                          'Approve Note: %s')}
        self = self.sudo()
        workflow_setting = self.env.ref('workflow.workflow_default_setting', raise_if_not_found=False)
        compose_message_obj = self.env['mail.compose.message']
        partner_obj = self.env['res.partner']
        template_obj = self.env['mail.template']
        for rec in self:
            submit_user = rec.wkf_line_instance_id.instance_id.approve_document.submit_user_id
            extend_message_ids = rec.wkf_line_instance_id.workflow_line_id.extend_message_ids
            partner = partner_obj
            template = template_obj
            msg_employee = None
            msg = ''
            detail_class = ''
            if mail_type == 'submitted':
                detail_class = 'submit'
                partner = submit_user.partner_id  # rec.apply_employee_id.user_id.partner_id
                template = self.env.ref('workflow.mail_template_submitted')
                if workflow_setting and template.id not in workflow_setting.mail_template_ids.ids:
                    continue
                if bool(set(['wechat', 'dingding', 'sms']) & set(extend_message_ids.mapped('code'))):
                    msg_employee = submit_user.related_employee_id
                    msg = workflow_message.get(mail_type) % rec.document_name
            elif mail_type == 'need_approval':
                detail_class = 'approve'
                partner = self.get_user(rec.allocated_approver_id.ids).mapped('partner_id')
                template = self.env.ref('workflow.mail_template_need_approval')
                if workflow_setting and template.id not in workflow_setting.mail_template_ids.ids:
                    continue
                if bool(set(['wechat', 'dingding', 'sms']) & set(extend_message_ids.mapped('code'))):
                    msg_employee = rec.allocated_approver_id
                    if self.has_install_module('kthrp_szxys_ext'):
                        msg = u'您有新的审批待处理。'
                    else:
                        msg = workflow_message.get(mail_type) % (rec.document_name, submit_user.name)
            elif mail_type == 'need_approval_transfer':
                detail_class = 'approve'
                partner = self.get_user(rec.allocated_approver_id.ids).mapped('partner_id')
                template = self.env.ref('workflow.mail_template_need_approval_transfer')
                if workflow_setting and template.id not in workflow_setting.mail_template_ids.ids:
                    continue
                if bool(set(['wechat', 'dingding', 'sms']) & set(extend_message_ids.mapped('code'))):
                    msg_employee = rec.allocated_approver_id
                    msg = workflow_message.get(mail_type) % (
                    rec.document_name, submit_user.name, rec.from_task_id.actual_approver_id.name)
            elif mail_type == 'need_approval_consult':
                detail_class = 'approve'
                partner = self.get_user(rec.allocated_approver_id.ids).mapped('partner_id')
                template = self.env.ref('workflow.mail_template_need_approval_consult')
                if workflow_setting and template.id not in workflow_setting.mail_template_ids.ids:
                    continue
                if bool(set(['wechat', 'dingding', 'sms']) & set(extend_message_ids.mapped('code'))):
                    msg_employee = rec.allocated_approver_id
                    msg = workflow_message.get(mail_type) % (
                    rec.document_name, submit_user.name, rec.from_task_id.actual_approver_id.name)
            elif mail_type == 'withdrawn':
                detail_class = 'notify1'
                partner = submit_user.partner_id
                template = self.env.ref('workflow.mail_template_withdrawn')
                if workflow_setting and template.id not in workflow_setting.mail_template_ids.ids:
                    continue
                if bool(set(['wechat', 'dingding', 'sms']) & set(extend_message_ids.mapped('code'))):
                    msg_employee = submit_user.related_employee_id
                    msg = workflow_message.get(mail_type) % rec.document_name
            elif mail_type == 'approved':
                detail_class = 'approved'
                partner = submit_user.partner_id
                template = self.env.ref('workflow.mail_template_approved')
                if workflow_setting and template.id not in workflow_setting.mail_template_ids.ids:
                    return
                if bool(set(['wechat', 'dingding', 'sms']) & set(extend_message_ids.mapped('code'))):
                    msg_employee = submit_user.related_employee_id
                    msg = workflow_message.get(mail_type) % rec.document_name
            elif mail_type == 'notify':
                detail_class = 'notify1'
                employee = self.env.context.get('partner_ids')
                partner = self.get_user(employee.ids).mapped('partner_id')
                template = self.env.ref('workflow.mail_template_notify')
                if workflow_setting and template.id not in workflow_setting.mail_template_ids.ids:
                    continue
                if bool(set(['wechat', 'dingding', 'sms']) & set(extend_message_ids.mapped('code'))):
                    msg_employee = employee
                    if self.has_install_module('kthrp_szxys_ext'):
                        msg = u'申请 %s 已被 %s 处理，请知悉!' % (rec.document_name, rec.actual_approver_id.name)
                    else:
                        msg = workflow_message.get(mail_type) % (rec.submit_user_id.name, rec.document_name, rec.actual_approver_id.name)
            elif mail_type == 'submit_notify':
                detail_class = 'notify1'
                employee = self.env.context.get('partner_ids')
                partner = self.get_user(employee.ids).mapped('partner_id')
                template = self.env.ref('workflow.mail_template_submit_notify')
                if workflow_setting and template.id not in workflow_setting.mail_template_ids.ids:
                    continue
                if bool(set(['wechat', 'dingding', 'sms']) & set(extend_message_ids.mapped('code'))):
                    msg_employee = employee
                    msg = workflow_message.get(mail_type) % rec.document_name
            elif mail_type == 'been_approved':
                detail_class = 'notify1'
                partner = submit_user.partner_id
                template = self.env.ref('workflow.mail_template_been_approved')
                if workflow_setting and template.id not in workflow_setting.mail_template_ids.ids:
                    continue
                # 提交人收取消息根据开始节点 是否配置额外通知确定
                extend_message_ids = rec.approve_document.task_ids[0].wkf_line_instance_id.workflow_line_id.extend_message_ids
                if bool(set(['wechat', 'dingding', 'sms']) & set(extend_message_ids.mapped('code'))):
                    msg_employee = submit_user.related_employee_id
                    msg = workflow_message.get(mail_type) % (rec.document_name or 'None', rec.actual_approver_id.name or 'unknown')
            elif mail_type == 'rejected':
                detail_class = 'reject'
                partner = submit_user.partner_id
                template = self.env.ref('workflow.mail_template_rejected')
                if workflow_setting and template.id not in workflow_setting.mail_template_ids.ids:
                    continue
                if bool(set(['wechat', 'dingding', 'sms']) & set(extend_message_ids.mapped('code'))):
                    msg_employee = submit_user.related_employee_id
                    msg = workflow_message.get(mail_type) % (rec.document_name, rec.actual_approver_id.name,
                                                             rec.approve_note)
            msg += rec.get_note()
            if template:
                tmp_obj = {
                    'model': rec.approve_document._name,
                    'partner_ids': [(6, 0, partner.ids)],
                    'res_id': rec.approve_document.id,
                    'template_id': template.id,
                    'subtype_id': self.sudo().env.ref('workflow.mail_message_workflow').id,
                    'composition_mode': 'comment',
                    'body': template.body_html,
                    'red': True if True in rec.wkf_line_instance_id.workflow_line_id.out_transition_ids.mapped('red') else False,
                }
                # 此处因为使用了suspend_security()跳权限，所以当前用户转变为了admin，从而发件邮箱为admin的工作邮箱
                mail = compose_message_obj.suspend_security().create(tmp_obj)
                render_values = \
                    compose_message_obj.suspend_security().onchange_template_id(mail.template_id.id,
                                                                                mail.composition_mode,
                                                                                mail.model, rec.id)['value']
                mail.body = render_values['body']
                if 'subject' in render_values.keys():
                    mail.subject = render_values['subject']
                else:
                    mail.subject = False
                send_actual_email = False
                if extend_message_ids:
                    if 'email' in extend_message_ids.mapped('code'):
                        send_actual_email = True
                    if 'wechat' in extend_message_ids.mapped('code'):
                        rec.send_wechat_msg(msg, msg_employee, mail_type, rec)
                    if 'dingding' in extend_message_ids.mapped('code'):
                        rec.send_dingding_msg(msg, msg_employee, mail_type, rec)
                    if 'sms' in extend_message_ids.mapped('code'):
                        self.env['sms.interface'].start_interface(content=msg, telephone=msg_employee.work_phone or msg_employee.mobile_phone or '')
                mail.send_mail(send_actual_email=send_actual_email, workflow_email=True, detail_class=detail_class)

    @property
    def is_finally_approve(self):
        if self.wkf_line_instance_id.workflow_line_id.out_transition_ids:
            if self.wkf_line_instance_id.workflow_line_id.countersign:
                orther_tasks = self.search([('wkf_line_instance_state', '=', 'processing'),
                                            ('wkf_line_instance_id.instance_id', '=',
                                             self.wkf_line_instance_id.instance_id.id),
                                            ('allocated_approver_id', '!=', self.actual_approver_id.id)])
                if orther_tasks:
                    return False
                else:
                    return True
            else:
                not_end_transition = self.wkf_line_instance_id.workflow_line_id.out_transition_ids.filtered(
                    lambda aml: not aml.to_workflow_line_id.is_end)
                if not_end_transition:
                    model = self.wkf_line_instance_id.instance_id.approve_document._name
                    r_id = self.wkf_line_instance_id.instance_id.approve_document.id
                    if any(not rec.wkf_condition_id or rec.wkf_condition_id.check_match_domain(model, r_id) is True for
                           rec in not_end_transition):
                        return False
                    else:
                        return True
                else:
                    return True
        else:
            return False

    @api.model
    def get_approve_count(self):
        count = self.search_count([('wkf_line_instance_state', '=', 'processing'),
                                   ('approve_result_id', '=', False),
                                   ('allocated_user_id', '=', self.env.user.id)])
        return count


    # @graph_profile('/opt/odoo/2.profile')
    @api.model
    def load_views(self, views, options=None):
        return super(WorkflowTask, self).load_views(views=views, options=options)

    # @log_profile
    @api.model
    def fields_view_get(self, view_id=None, view_type=False, toolbar=False, submenu=False):
        if view_type == 'form':
            form_view = self.env.context.get('_form_view', False)
            if form_view:
                view = self.env.ref(form_view)
                view_id = view.id
        res = super(WorkflowTask, self).fields_view_get(view_id=view_id, view_type=view_type,
                                                                 toolbar=toolbar, submenu=submenu)
        return res

    @api.multi
    def send_wechat_msg(self, msg, msg_employee, mail_type, task_id):
        pass

    @api.multi
    def send_dingding_msg(self, msg, msg_employee, mail_type, task_id):
        pass

    @api.model
    def name_search(self, name='', args=None, operator='ilike', limit=100):
        args = args or []

        args += self._get_domains(args)
        # 放在最后过滤重复值
        # if self.env.context.get('duplicate_removal'):
        #     last_records = []
        #     uustr_list = []
        #     records = self.search(args, order='approve_time desc')
        #     for record in records:
        #         uustr = str(record.wkf_line_instance_id.instance_id.id) + str(record.actual_user_id.id)
        #         if uustr in uustr_list:
        #             continue
        #         else:
        #             uustr_list.append(uustr)
        #             last_records.append(record.ids)
        #     if last_records:
        #         args += [('id', 'in', last_records)]
        self.process_domain(args)
        if isinstance(limit, list):
            if len(limit) >= 1:
                limit = limit[0]
            else:
                limit = None
        return super(WorkflowTask, self).name_search(name, args=args, operator=operator,
                                                              limit=limit)

    # @graph_profile('/opt/odoo/1.profile')
    # @log_profile
    @api.model
    def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None):
        domain = domain or []
        domain += self._get_domains(domain)
        # 放在最后过滤重复值
        self.process_domain(domain)
        if isinstance(limit, list):
            if len(limit) >= 1:
                limit = limit[0]
            else:
                limit = None
        return super(WorkflowTask, self).search_read(domain=domain, fields=fields, offset=offset, limit=limit,
                                                              order=order)

    @api.model
    def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
        domain = domain or []
        self.process_domain(domain)
        domain += self._get_domains(domain)
        # 放在最后过滤重复值


        if isinstance(limit, list):
            if len(limit) >= 1:
                limit = limit[0]
            else:
                limit = None
        return super(WorkflowTask, self).read_group(domain=domain, fields=fields, groupby=groupby,
                                                             offset=offset,
                                                             limit=limit, orderby=orderby, lazy=lazy)

    @api.model
    def search_count(self, args):
        domain = args or []
        domain += self._get_domains(domain)
        self.process_domain(domain)
        # 放在最后过滤重复值

        return super(WorkflowTask, self).search_count(domain)

    # 处理特殊值
    @api.model
    def process_domain(self, domain):
        check_list = ('P/', 'E/', 'B/', 'C/', 'CA/', 'CSA/')
        for d in domain:
            if 'document_name' in d:
                if d[-1]:
                    for check_l in check_list:
                        if d[-1].startswith(check_l):
                            d[-1] = d[-1][len(check_l):]
                            break
        return domain

    @api.model
    def _get_domains(self, domain):
        domain_addition = []

        if self.env.context.get('duplicate_removal'):
            self._cr.execute('''
                select
                case 
                    when count(1) = 1 THEN
                        max(wt.id)
                    when count(1) > 1 then
                (select wt1.id from workflow_task wt1, workflow_instance_line il1 where wt1.wkf_line_instance_id = il1.id
                and wt1.actual_user_id = wt.actual_user_id and  wt1.document_name = wt.document_name order by wt1.approve_time desc limit 1)
                END
                 latest_task_id
                From workflow_task wt
                , workflow_instance_line il
                where wt.wkf_line_instance_id = il.id
                and wt.actual_user_id = %s
                group by wt.document_name, wt.actual_user_id
            ''', (self.env.user.id, ))

            last_task_ids = []
            for task in self._cr.dictfetchall():
                last_task_ids.append(task.get('latest_task_id'))

            domain_addition += [('id', 'in', last_task_ids)]

        if self.env.context.get('line_notified_document'):
            sql = '''
            select max(kbwt.id) as task_id from workflow_task kbwt,hr_employee_workflow_task_rel hewt 
            where kbwt.id = hewt.workflow_task_id and hewt.hr_employee_id = %s
            group by kbwt.document_name  
            '''
            self._cr.execute(sql, (self.env.user.related_employee_id.id,))
            results = self._cr.dictfetchall()

            notified_document_ids = []
            for result in results:
                notified_document_ids.append(result.get('task_id'))

            if self.env.context.get('allocated_notified_document'):
                sql = '''
                select max(id) as task_id from workflow_task 
                where allocated_user_id = %s
                group by document_name
                '''
                self._cr.execute(sql, (self.env.user.id,))
                results = self._cr.dictfetchall()

                for result in results:
                    notified_document_ids.append(result.get('task_id'))

            domain_addition += [('id', 'in', tuple(notified_document_ids))]

        return domain_addition

    def send_task_msg_remind(self):
        sql = '''
        select count(id) as amount,allocated_approver_id from workflow_task
        where wkf_line_instance_state = 'processing' and approve_result_id is null 
        group by allocated_approver_id
        '''
        self.env.cr.execute(sql)
        results = self.env.cr.dictfetchall()
        for result in results:
            self.with_delay().do_send_task_msg(result)

    @job
    def do_send_task_msg(self, result):
        employee_obj = self.env['hr.employee'].browse(result.get('allocated_approver_id'))
        subject = '【HRP系统】待审批流程提醒'
        cron_date = str(fields.date.today())
        message = '截至到【{cron_date}】，您有{amount}条待审批记录，请及时处理。'.format(cron_date=cron_date, amount=result.get('amount'))
        if self.has_install_module('kthrp_dingding'):
            userids = [employee_obj.dingding_code]
            # 转为 markdown 语法
            msg = "# %s\n%s" % (subject, message)
            self.env['kthrp.dingding.user'].common_send_ding_msg(userids, msg, msgtype='action_card', subject=subject)
        if self.has_install_module('kthrp_wechat'):
            enterprise_wechat = [employee_obj.enterprise_wechat]
            url = self.env['kthrp.wechat.agent'].get_to_be_approved_url(employee_obj.enterprise_wechat)
            self.env['kthrp.wechat.agent'].send_common_text_msg(enterprise_wechat, message, subject=subject ,send_type='text_card', url=url)


class WorkflowSignal(models.Model):
    _name = "workflow.signal"
    _description = "Kthrp Base Workflow Signal"

    name = fields.Char(string="Name", required=True, translate=True)
    code = fields.Char(string="Code", required=True)
    type = fields.Selection(selection=[('normal', _('Normal')),
                                       ('special', _('Special'))],
                            string='Type',
                            required=True)


class WorkflowExtendMessage(models.Model):
    _name = "workflow.extend.message"
    _description = "Kthrp Base Workflow Extend Message"

    name = fields.Char(string="Name", required=True, translate=True)
    code = fields.Char(string="Code", required=True)

    @api.model
    def name_search(self, name='', args=None, operator='ilike', limit=100):
        if args is None:
            args = []
        if not self._context.get('show_wechat', False):
            args.append(('code', '!=', 'wechat'))
        if not self._context.get('show_dingding', False):
            args.append(('code', '!=', 'dingding'))
        return super(WorkflowExtendMessage, self).name_search(name, args=args, operator=operator, limit=limit)


class WorkflowBatchTransferRecords(models.Model):
    _name = "workflow.batch.transfer.records"
    _description = "Kthrp Base Workflow Approval Authority Transfer"
    _inherit = "workflow.interface"
    _rec_name = "doc_number"
    _going_live_clear_cls = 'clearable'

    def _get_current_employee(self):
        return self.env.user.related_employee_id

    def _get_workflow_model_ids(self):
        records = self.env['workflow.model'].sudo().search([])
        return records.ids

    doc_number = fields.Char(string='Doc. Number', index=True)
    processor_id = fields.Many2one(comodel_name='hr.employee', string='Processed By', default=_get_current_employee)
    transfer_type = fields.Selection([('resign', 'Resign'),
                                      ('leave', 'Leave'),
                                      ('other', 'Other')], string='Transfer Type',
                                     required=1)
    process_time = fields.Datetime(string='Process Time', default=fields.Datetime.now)
    transfer_from_id = fields.Many2one(comodel_name='hr.employee', string='Transfer From',
                                       default=_get_current_employee)
    transfer_to_id = fields.Many2one(comodel_name='hr.employee', string='Transfer To')
    today = fields.Datetime(compute='_compute_now', copy=False)
    start_date = fields.Datetime(string='Start Date', copy=False, default=fields.Datetime.now)
    end_date = fields.Datetime(string='End Date', copy=False)
    transfer_reason = fields.Text(string='Transfer Reason')
    workflow_model_ids = fields.Many2many(comodel_name='workflow.model',
                                          relation='kthrp_transfer_records_workflow_model_ref',
                                          column1='transfer_id',
                                          column2='model_id',
                                          string='Approve Document',
                                          default=_get_workflow_model_ids)
    state = fields.Selection(selection=[('created', 'Created'),
                                        ('submitted', 'Submitted'),
                                        ('approving', 'Approving'),
                                        ('rejected', 'Rejected'),
                                        ('approved', 'Approved'),
                                        ('cancelled', 'Cancelled')],
                             index=True,
                             default='created', required=True)

    def _get_default_can_edit(self):
        return self.env.user.has_group('workflow.group_wf_transfer_from')

    can_edit = fields.Boolean(default=_get_default_can_edit, compute='_compute_can_edit')

    @api.multi
    def _compute_can_edit(self):
        can_edit = self.env.user.has_group('workflow.group_wf_transfer_from')
        for rec in self:
            rec.can_edit = can_edit

    @api.multi
    @api.onchange('transfer_from_id')
    def _onchange_transfer_from_id(self):
        for rec in self:
            rec.transfer_to_id = False

    @api.multi
    @api.constrains('transfer_from_id', 'transfer_to_id')
    def _check_transfer_to(self):
        for rec in self:
            ctx = {
                'same_organization': True,
                'transfer_from_id': rec.transfer_from_id.id or False,
                'workflow_transfer': True
            }
            domain = [("status", "not in", ("resign", "retirement")),('id','=',rec.transfer_to_id.id)]
            if self.env['hr.employee'].with_context(**ctx).search_count(domain) <= 0:
                raise ValidationError(_t(self.env.user.lang, ['The Referred people did not meet the requirements', '转交至人员不符合要求']))


    @api.multi
    @api.constrains('transfer_from_id', 'workflow_model_ids', 'start_date')
    def _check_transfer(self):
        transfer_obj = self.env['workflow.batch.transfer.records']
        now = fields.Datetime.now()
        for rec in self:
            if rec.start_date:
                model_ids = rec.workflow_model_ids.ids
                other_transfers = transfer_obj.search([('transfer_from_id', '=', rec.transfer_from_id.id),
                                                       ('id', '!=', rec.id),
                                                       ('state', '!=', 'cancelled'),
                                                       ('start_date', '!=', False),
                                                       '|',
                                                       ('end_date', '=', False),
                                                       ('end_date', '>=', now)])
                for other_transfer in other_transfers:
                    if other_transfer.workflow_model_ids:
                        total_model = len(other_transfer.workflow_model_ids.ids) + len(model_ids)
                        if len(set(other_transfer.workflow_model_ids.ids + model_ids)) != total_model:
                            raise ValidationError(
                                _('There are unexpired transfer records of document %s.') % other_transfer.doc_number)

    @api.multi
    def _compute_now(self):
        today = fields.Datetime.now()
        for rec in self:
            rec.today = today

    # 提交逻辑
    @api.multi
    def action_submit(self):
        # 跳过创建工作流
        if self.env.context.get('pass_create_wkf'):
            pass
        else:
            # 实现单据提交跳转输入审批人和备注的功能。prepare_submit参数需要和提交方法名对应上;
            ok = self.prepare_submit('action_submit')
            if ok:
                return ok
            for rec in self:
                rec.state = 'submitted'
                self.env['workflow'].create_or_restart_wkf(self._name, self.id)

    # 重置为草稿
    @api.multi
    def action_to_draft(self):
        res = super(WorkflowBatchTransferRecords, self).action_to_draft()
        for rec in self:
            rec.state = 'created'
        return res

    # 取消按钮逻辑
    @api.multi
    def action_cancel(self):
        for rec in self:
            rec.state = 'cancelled'

    @api.multi
    def unlink(self):
        for rec in self:
            if rec.state != 'created':
                raise ValidationError(_('You can only delete document in draft status.'))
        return super(WorkflowBatchTransferRecords, self).unlink()

    @api.model
    def create(self, vals):
        if not vals.get('doc_number'):
            vals['doc_number'] = self.env.ref('workflow.sequence_authority_transfer').next_by_id()
        return super(WorkflowBatchTransferRecords, self).create(vals)

    @api.multi
    def write(self, vals):
        res = super(WorkflowBatchTransferRecords, self).write(vals)
        for rec in self:
            if not rec.doc_number:
                rec.doc_number = vals['doc_number'] = self.env.ref(
                    'workflow.sequence_authority_transfer').next_by_id()
        return res

    @api.multi
    def copy(self, default=None):
        self.ensure_one()
        default = dict(default or {})
        if self.workflow_model_ids:
            default['workflow_model_ids'] = [[6, 0, self.workflow_model_ids.ids]]
        default['doc_number'] = self.env.ref('workflow.sequence_authority_transfer').next_by_id()
        return super(WorkflowBatchTransferRecords, self).copy(default)

    @api.model
    def transfer_employee(self, employee_id, record):
        result = []
        if not employee_id or not record:
            return []
        if record and len(record) == 1:
            result.append([record.transfer_to_id.id, record.get_transfer_info_string()])
        else:
            result.append([employee_id, ''])
        return result

    @api.model
    def transfer_employee_test(self, employee_id, record):
        result = []
        if not employee_id or not record:
            return []
        if record and len(record) == 1:
            result.append(record.transfer_to_id.id)
        else:
            result.append(employee_id)
        return result

    @api.multi
    def get_transfer_info_string(self):
        self.ensure_one()
        if self.transfer_type == 'resign':
            template_str = _('%s Resign Transfer to %s')
        elif self.transfer_type == 'leave':
            template_str = _('%s Leave Transfer to %s')
        else:
            template_str = _('%s Transfer to %s')
        final_str = template_str % (self.transfer_from_id.name, self.transfer_to_id.name) or ''
        return final_str

    @api.model
    def get_transfer_record(self, wkf_model_id, approve_id):
        wkf_model = self.env['workflow.model'].search([('model_id', '=', wkf_model_id)], limit=1)
        if not wkf_model:
            return False
        records = self.search([('transfer_from_id', '=', approve_id),
                               ('start_date', '<=', fields.Datetime.now()),
                               ('state', '=', 'approved'),
                               '|',
                               ('end_date', '>=', fields.Datetime.now()),
                               ('end_date', '=', False)])
        for record in records:
            if wkf_model.id in record.workflow_model_ids.ids:
                return record
        return False

    @api.multi
    def open_document(self):
        action = self.env.ref('workflow.action_workflow_batch_transfer_records_for_open')
        action.sudo().context = self.env.context
        act = action.simple_read()[0]
        act['res_id'] = self.id
        return act

    @api.model
    def _get_domains(self):
        domain = []
        if self.env.context.get('self_doc'):
            user = self.env.user
            domain += ['|', ('processor_id', '=', user.related_employee_id.id),
                       ('transfer_from_id', '=', user.related_employee_id.id)]

        return domain

    @api.model
    def name_search(self, name='', args=None, operator='ilike', limit=100):
        args = args or []
        args += self._get_domains()
        return super(WorkflowBatchTransferRecords, self).name_search(name, args=args, operator=operator,
                                                                              limit=limit)

    @api.model
    def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None):
        domain = domain or []
        domain += self._get_domains()
        return super(WorkflowBatchTransferRecords, self).search_read(domain=domain, fields=fields,
                                                                              offset=offset,
                                                                              limit=limit,
                                                                              order=order)

    @api.model
    def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
        domain = domain or []
        domain += self._get_domains()
        return super(WorkflowBatchTransferRecords, self).read_group(domain=domain, fields=fields,
                                                                             groupby=groupby,
                                                                             offset=offset,
                                                                             limit=limit, orderby=orderby, lazy=lazy)

    @api.model
    def search_count(self, args):
        domain = args or []
        domain += self._get_domains()
        return super(WorkflowBatchTransferRecords, self).search_count(domain)


class WorkflowSubstitutionRule(models.Model):
    _name = "workflow.substitution.rule"
    _description = "Kthrp Base Workflow Substitution Rule"
    _order = 'sequence'

    sequence = fields.Integer(string='Sequence', default=1, required=True)
    wkf_condition_id = fields.Many2one(comodel_name='workflow.condition', string='Substitution Condition',
                                       ondelete='restrict', required=True)
    substitution_type = fields.Selection([('approve_role', 'Approve Role'), ('designated_person', 'Designated Person')],
                                         default='approve_role',
                                         required=True,
                                         string='Substitution Type')
    substitution_sel = fields.Char()
    substitution = fields.Reference(selection='_select_objects', string="Substitution Approver", required=True)
    wkf_model_id = fields.Many2one(comodel_name='ir.model', string="Model", readonly=True,
                                   compute='_compute_wfk_model_id')
    workflow_line_id = fields.Many2one(comodel_name='workflow.line', string='Workflow Line',
                                       ondelete='cascade',
                                       copy=False,
                                       domain=[('node_type', 'not in', ('start', 'end'))])

    @api.multi
    @api.depends('sequence', 'workflow_line_id')
    def _compute_wfk_model_id(self):
        for rec in self:
            wkf_id = self.env.context.get('_wkf_id')
            if wkf_id:
                rec.wkf_model_id = self.env['workflow'].browse(wkf_id).wkf_model_id.id
            else:
                rec.wkf_model_id = rec.workflow_line_id.workflow_id.wkf_model_id.id

    @api.multi
    @api.onchange('substitution_type')
    def _onchange_substitution_type(self):
        for rec in self:
            if rec.substitution_type == 'approve_role':
                rec.substitution_sel = 'workflow.role'
            elif rec.substitution_type == 'designated_person':
                rec.substitution_sel = 'hr.employee'

    @api.model
    def _select_objects(self):
        models = ('workflow.role', 'hr.employee')
        records = self.env['ir.model'].search([('model', 'in', models)])
        return [(record.model, record.name) for record in records] + [('', '')]

    @api.multi
    def get_substitution_approver(self, instance):
        self = self.suspend_security()
        role_obj = self.env['workflow.role']
        result = None
        if self.substitution_type == 'approve_role':
            result_temp = role_obj.get_role_approver_direct(instance, self.substitution)
            if result_temp:
                result = result_temp[0].id
        else:
            result = self.substitution.id
        if not result:
            raise ValidationError(_(
                'No approver can be found because of the substitution rules, please contact the system administrator.'))
        return result


class WorkflowSettings(models.Model):
    _name = "workflow.settings"
    _description = "Kthrp Base Workflow Settings"

    name = fields.Char(translate='True')

    def _get_default_mail(self):
        templates = self.env['mail.template'].sudo().search([('model_id.model', '=', 'workflow.task')])
        return templates.ids

    mail_template_ids = fields.Many2many(comodel_name='mail.template', string='External Notification Template',
                                         domain="[('model_id.model','=','workflow.task')]",
                                         default=_get_default_mail)
    default_of_return = fields.Boolean(string='Default of Return', default=True,
                                       help='If checked, the workflow will default to return when transfer.')
    default_of_skip_approved_node = fields.Boolean(string='Default of Skip Approved Node', default=False,
                                                   help='If checked, the field “Skip Approved Node When Re-submit” will default from here when rejecting.')

    external_links_clickable = fields.Boolean(string='External Links Clickable', default=True,
                                              help='When checked, the link provided in the message and the body of the email can be clicked to jump. If it is cross-company or cross-permission, there will be corresponding prompt.')

    delay_send_email = fields.Boolean(sting='Delay Send Email')

    delay_minutes = fields.Integer(string='Delay Minutes')

    automatic_skip_after_approval = fields.Selection(selection=[('to_be_approved', 'To Be Approved'),
                                                                ('portal', 'Portal'),
                                                                ('stay', 'Stay')
                                                                ],
                                                     required=True,
                                                     string='Automatic Skip After Approval',
                                                     default='to_be_approved',
                                                     help='If checked, the interface will skip to Tobe Approving list after processed.')

    submitter_as_first_approver = fields.Selection([('auto_approve', 'Auto Approve'),
                                                    ('auto_send_to_direct_leader', 'Auto Send To Direct Leader')],
                                                   string='Submitter As First Approver',
                                                   default='auto_approve',
                                                   help='After selection, if the approver of the first node is the same as the submitter, the approver will be automatically replaced according to the rules.')
    skip_submitter_approve = fields.Boolean(string='Skip submitter approve', default=False)
    enable_again_submit_note = fields.Boolean(string='Resubmit input Note', default=False)
    allow_withdraw_before_next_approve = fields.Boolean(string='Allow withdrawal before approval of the next node',
                                                        default=False)

    @api.multi
    @api.onchange('delay_send_email')
    def _onchange_delay_send_email(self):
        for rec in self:
            if rec.delay_send_email:
                rec.delay_minutes = 1
            else:
                rec.delay_minutes = False

    @api.multi
    def after_process_task_return(self):
        self = self.suspend_security()
        if self.automatic_skip_after_approval == 'to_be_approved':
            return_action = self.env.ref('workflow.action_workflow_task_to_be_approved')
            act = return_action.read(
                ['name', 'type', 'res_model', 'view_type', 'view_mode', 'target', 'domain', 'context', 'view_id',
                 'search_view_id'])[0]
            if act.get('context') and act.get('context') != 'False':
                context = eval(act['context'])
                context['clear_breadcrumbs'] = True
                context['search_disable_custom_filters'] = False
                context['workflow_approving'] = True
                context = str(context).decode('gbk')
                act['context'] = context
            else:
                act['context'] = ("{'clear_breadcrumbs':True,'workflow_approving':True}").decode('gbk')
            act['domain'] = [('allocated_user_id', '=', self.env.user.id)]
            return act
        elif self.automatic_skip_after_approval == 'portal':
            return_action = self.env.ref('kthrp_portal.action_homepage', raise_if_not_found=False)
            act = return_action.read()[0]
            if act.get('context') and act.get('context') != 'False':
                context = eval(act['context'])
                context['clear_breadcrumbs'] = True
                context = str(context).decode('gbk')
                act['context'] = context
            return act
        else:
            return


class WorkflowEmailQueue(models.Model):
    _name = "workflow.email.queue"
    _description = "Kthrp Base Workflow Email Queue"
    _rec_name = 'id'

    task_id = fields.Many2one(comodel_name='workflow.task', string='Approve Task',
                              ondelete='cascade', index=True)
    mail_type = fields.Selection(selection=[('submitted', 'Submitted'),
                                            ('need_approval', 'Need Approval'),
                                            ('need_approval_transfer', 'Need Approval Transfer'),
                                            ('need_approval_consult', 'Need Approval Consult'),
                                            ('withdrawn', 'Withdrawn'),
                                            ('approved', 'Approved'),
                                            ('been_approved', 'Been Approved'),
                                            ('notify', 'Notify'),
                                            ('submit_notify', 'Submit Notify'),
                                            ('rejected', 'Rejected')], string='Mail type', required=True)

    context = fields.Text(string='Context')

    state = fields.Selection([('not_send', 'Not Send'),
                              ('sent', 'Sent'),
                              ('error', 'Error')], string='State', default='not_send')

    err_msg = fields.Text(string='Error Message')

    timestamp = fields.Integer(string='timestamp', index=True)

    document_name = fields.Char(string='Approve Document', readonly=True)



    @api.multi
    def re_send(self):
        self = self.suspend_security()
        queues = self.env['workflow.email.queue']
        for rec in self:
            if rec.state == 'sent':
                continue
            if rec.task_id:
                try:
                    if not (rec.task_id.wkf_line_instance_id.instance_id.state == 'withdrawn' and rec.mail_type != 'withdrawn'):
                        context = safe_eval(rec.context.replace('hr.employee', '') or '{}')
                        if context.get('partner_ids'):
                            context['partner_ids'] = self.env['hr.employee'].browse(list(context.get('partner_ids')))
                        rec.task_id._wkf_send_email_job(rec.mail_type,  context or self.env.context)
                    queues += rec
                except Exception as e:
                    self.env.cr.commit()
                    buff = StringIO()
                    traceback.print_exc(file=buff)
                    err = str(buff.getvalue())
                    rec.write({'state':'error', 'err_msg': err})
        if queues:
            queues.write({'state':'sent', 'err_msg': False})

    @api.model
    def cron_send_queue_email(self):
        self = self.suspend_security()
        x_timestamp = int(time.time())
        mails = self.search([('timestamp','<=', x_timestamp),
                             ('state', 'in', ('not_send','error')),
                             ('task_id', '!=', False)])
        # 三天了还在报错，别发了
        alone_error_mails = mails.filtered(lambda x:x.state == 'error' and x.timestamp + 259200 <= x_timestamp)
        alone_error_mails.write({'state': 'sent'})
        if mails:
            mails.re_send()


class WorkflowGraphLocation(models.Model):
    _name = "workflow.graph.location"
    _log_access = False

    workflow_id = fields.Integer(string='Workflow', index=True)

    location = fields.Text(string='Location')

    @api.model
    def save_workflow_graph_location(self, args):
        workflow_id = args.get('workflow_id')
        location = args.get('location')
        workflow_img = args.get('workflow_img')
        self = self.suspend_security()
        workflow_obj = self.env['workflow'].browse(workflow_id)
        if workflow_obj and workflow_img:
            workflow_obj.write({'workflow_img':workflow_img[23:]})
        exist_record = self.search([('workflow_id', '=', workflow_id)])
        if exist_record:
            exist_record.write({'location': str(location)})
        else:
            self.create({'workflow_id': workflow_id, 'location': str(location)})

    @api.model
    def get_graph_location_by_workflow_id(self, workflow_id):
        self = self.suspend_security()
        exist_record = self.search([('workflow_id', '=', workflow_id)], limit=1)
        if exist_record:
            result = safe_eval(exist_record.location)
        else:
            result = None
        return result


class WorkflowSavedDocument(models.Model):
    _name = "workflow.saved.document"
    _description = "Kthrp Base Workflow Saved Document"

    document = fields.Reference(string='Document', selection='_select_objects', context={'for_task':True,'skip':True})
    document_name_char = fields.Char(string='Document',compute='_compute_document_name')
    document_name_char_store = fields.Char(string='Document')
    model_id = fields.Many2one(comodel_name='ir.model', string='Document Name')
    company_id = fields.Many2one(comodel_name='res.company', string='Company',
                                 compute='_compute_document_company', store=True)
    active = fields.Boolean(string='Active', default=True)

    @api.model
    def _select_objects(self):
        records = self.env['ir.model'].search([])
        return [(record.model, record.name) for record in records] + [('', '')]

    @api.multi
    @api.depends('document')
    def _compute_document_name(self):
        lang = self.env.user.lang
        for rec in self:
            try:
                if rec.document:
                    approve_document_model = rec.document._name
                    # if approve_document_model == 'kthrp.hr.leave.record':
                    #     document_type = '休假申请' if lang == 'zh_CN' else 'Leave App'
                    # elif approve_document_model == 'kthrp.hr.resumption.leave.application':
                    #     document_type = '销假申请' if lang == 'zh_CN' else 'Resumption Leave App'
                    # elif approve_document_model == 'kthrp.hr.outwork.application':
                    #     document_type = '外出申请' if lang == 'zh_CN' else 'Outwork App'
                    # elif approve_document_model == 'kthrp.hr.employee.change.apply' and rec.document.change_type == 'become a full member':
                    #     document_type = '转正申请' if lang == 'zh_CN' else 'Become A Full Member App'
                    # elif approve_document_model == 'kthrp.hr.employee.change.apply' and rec.document.change_type == 'promotion':
                    #     document_type = '晋升申请' if lang == 'zh_CN' else 'Promotion App'
                    # elif approve_document_model == 'kthrp.hr.employee.change.apply' and rec.document.change_type == 'job transfer':
                    #     document_type = '调岗申请' if lang == 'zh_CN' else 'Job Transfer App'
                    # elif approve_document_model == 'kthrp.hr.employee.change.resign':
                    #     document_type = '离职申请' if lang == 'zh_CN' else 'Resign App'
                    # else:
                    #     document_type = ''
                    rec.document_name_char = (str(
                        rec.document.with_context(for_task=True, skip=True,lang=lang).name_get()[0][1]) or 'N/A')
            except Exception as e:
                pass


    @api.multi
    def open_document(self):
        self.ensure_one()
        self = self.suspend_security()
        ctx = self.env.context.copy()

        if self.document:
            ctx.update({'active_model': self.document._name, 'active_ids': self.document.ids,
                        'active_id': self.document.id})
            return self.with_context(ctx).document.open_document()

    @api.multi
    @api.depends('document')
    def _compute_document_company(self):
        for rec in self:
            try:
                if rec.document:
                    document = rec.document.suspend_security()
                    if hasattr(document, 'company_id'):
                        rec.company_id = document.company_id.id
                    elif hasattr(document, 'legal_company_id'):
                        rec.company_id = document.legal_company_id.id
                    elif hasattr(document, 'x_company_id'):
                        rec.company_id = document.x_company_id.id
                    elif hasattr(document, 'x_company'):
                        rec.company_id = document.x_company.id
            except Exception as e:
                pass


    @api.model
    def inactive_saved_document(self, model_name, record_id):
        document = model_name + ',' + str(record_id)
        sql = '''UPDATE workflow_saved_document SET active='f' WHERE document=%s'''
        self.env.cr.execute(sql, (document,))

    @api.model
    def active_saved_document(self, model_name, record_id):
        document = model_name + ',' + str(record_id)
        query_sql = '''SELECT COUNT(*) FROM workflow_saved_document WHERE document=%s'''
        self.env.cr.execute(query_sql, (document,))
        count = self.env.cr.fetchall()[0][0]
        if count:
            sql = '''UPDATE workflow_saved_document SET active='t' WHERE document=%s'''
            self.env.cr.execute(sql, (document,))
        else:
            if model_name != 'workflow.task':
                doc_vals = {'document': model_name + ',' + str(record_id),
                            'model_id': self.env['ir.model'].search([('model', '=', model_name)], limit=1).id
                            }
                self.create(doc_vals)

    @api.model
    def delete_saved_document(self, model_name, record_id):
        document = model_name + ',' + str(record_id)
        sql = '''DELETE FROM  workflow_saved_document WHERE document=%s'''
        self.env.cr.execute(sql, (document,))


    @api.model
    def _get_domains(self):
        uid = self.env.user.id
        all_documents = self.search([('create_uid', '=', uid)])
        domain = []
        id_list = []
        for all_document in all_documents.with_context(prefetch_fields=False):
            if hasattr(all_document.document, 'state'):
                if all_document.document.state  in ('cancelled', 'processed', 'part_processed'):
                    id_list.append(all_document.id)
        if id_list:
            domain += [('id', 'not in', id_list)]
        return domain

    @api.model
    def name_search(self, name='', args=None, operator='ilike', limit=100):
        args = args or []
        domain = []
        domain += self._get_domains()
        return super(WorkflowSavedDocument, self).name_search(name, args=args + domain, operator=operator,
                                                                     limit=limit)

    @api.model
    def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None):
        domain = domain or []
        domain += self._get_domains()
        return super(WorkflowSavedDocument, self).search_read(domain=domain, fields=fields, offset=offset,
                                                                     limit=limit,
                                                                     order=order)

    @api.model
    def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
        domain = domain or []
        domain += self._get_domains()
        return super(WorkflowSavedDocument, self).read_group(domain=domain, fields=fields, groupby=groupby,
                                                                    offset=offset,
                                                                    limit=limit, orderby=orderby, lazy=lazy)

    @api.model
    def search_count(self, args):
        domain = args or []
        domain += self._get_domains()
        return super(WorkflowSavedDocument, self).search_count(domain)


class WorkflowCirculate(models.TransientModel):
    _name = "workflow.circulate"

    # 通知内容
    notify_note = fields.Text(string="Notify Note", default="")
    # 通知
    notify_ids = fields.Many2many(comodel_name="hr.employee", string="Notify", required=True)
    # 附件
    attachment_ids = fields.Many2many(comodel_name='ir.attachment', string="Attachment")
    # 已传阅用户
    circulated_users_ids = fields.Many2many(comodel_name="res.users", string="Circulated Users")

    def action_confirm(self):
        active_ids = self.env.context.get('active_ids', [])
        active_model = self.env.context.get('active_model', [])
        notify__datas = self.env[active_model].browse(active_ids)
        if notify__datas:
            for not_d in notify__datas:
                template = self.env.ref('workflow.mail_template_workflow_circulate_notice')
                partner_ids = self.env['res.users'].search([('related_employee_id', 'in', self.notify_ids.ids)]).mapped(
                                    'partner_id').ids
                author_id = self.env['mail.message']._get_default_author()
                if hasattr(self.env[active_model], 'doc_number'):
                    doc_number = not_d.doc_number
                elif hasattr(self.env[active_model], 'document_number'):
                    doc_number = not_d.document_number
                elif hasattr(self.env[active_model], 'document_no'):
                    doc_number = not_d.document_no
                elif hasattr(self.env[active_model], 'payment_doc_number'):
                    doc_number = not_d.payment_doc_number
                elif hasattr(self.env[active_model], 'x_doc_number'):
                    doc_number = not_d.x_doc_number
                elif hasattr(self.env[active_model], 'x_document_number'):
                    doc_number = not_d.x_document_number
                elif hasattr(self.env[active_model], 'x_num'):
                    doc_number = not_d.x_num
                elif hasattr(self.env[active_model], 'x_code'):
                    doc_number = not_d.x_code
                elif hasattr(self.env[active_model], 'x_name'):
                    doc_number = not_d.x_name
                else:
                    doc_number = ''
                doc_dict = {
                    'kthrp.sales.order.line': 'head_id',
                    'kthrp.expense.report.borrowing': 'expense_borrowing_id',
                    'kthrp.account.equity.invest.eliminatation': 'name',
                    'kthrp.stock.inventory': 'doc_no',
                    'kthrp.purchase.supplier.qualification': 'source_doc_number',
                    'company.qualification.interface': 'source_doc_number',
                    'kthrp.contract.contract': 'code',
                    'kthrp.contract.contract.without.price': 'code',
                }
                if active_model in doc_dict.keys():
                    doc_number = not_d.mapped(doc_dict[active_model])[0]
                model_name = self.env['ir.model'].search([('model','=',active_model)],limit=1).name
                body = template.with_context(author=author_id.display_name,
                                             notify_note=self.notify_note,
                                             active_model=active_model,
                                             doc_number=doc_number,
                                             model_name=model_name).render_template(template.body_html,
                                                                                           active_model, not_d.id)
                self.env['mail.thread'].sudo().with_context(lang=self.env.context.get('lang')).message_post(body=body, partner_ids=partner_ids,
                                                                                subtype_id=self.env.ref('workflow.mail_message_workflow').id,
                                                                                author_id=author_id.id,
                                                                                detail_class='notify2',
                                                                                res_model=not_d._name,
                                                                                subject=template.subject,
                                                                                res_id=not_d.id,
                                                                                attachment_ids=self.attachment_ids.ids)
                users_ids = self.env['res.users'].search([('related_employee_id', 'in', self.notify_ids.ids)]).ids
                history_obj = self.env['workflow.circulate.history']
                exist_history_obj = history_obj.search([('doc_model', '=', active_model), ('doc_id', '=', not_d.id)])
                if exist_history_obj:
                    exist_history_obj.circulated_users_ids = [(6,0,list(set(users_ids + exist_history_obj.circulated_users_ids.ids)))]
                else:
                    history_obj.create({
                        'doc_model': active_model,
                        'doc_id': not_d.id,
                        'circulate_time': fields.Datetime.now(),
                        'circulated_users_ids': [(4, uid) for uid in users_ids]
                    })

    @api.model
    def default_get(self, field_list):
        res = super(WorkflowCirculate, self).default_get(field_list)
        active_ids = self.env.context.get('active_ids', [])
        active_model = self.env.context.get('active_model', [])
        history_obj = self.env['workflow.circulate.history'].search([('doc_model', '=', active_model), ('doc_id', '=', active_ids[0])])
        if history_obj:
            res.update({
                'circulated_users_ids': history_obj.circulated_users_ids.ids
            })
        return res


class WorkflowCirculateHistory(models.Model):
    _name = "workflow.circulate.history"

    doc_model = fields.Char(string="Doc Model", required=True)
    doc_id = fields.Integer(string="Doc Id", required=True)
    circulate_time = fields.Datetime(string="Circulate Time")
    circulated_users_ids = fields.Many2many(comodel_name="res.users", string="Circulated Users", required=True)



class WorkflowBatchDownloadPhotos(models.TransientModel):
    _name = "workflow.batch.download.photos"

    def download_workflow_photos(self):
        workflow_obj = self.env['workflow']
        workflow_ids = self.env.context.get('active_ids', [])
        workflow = []
        err_name = ''
        if workflow_ids:
            for wo in workflow_obj.browse(workflow_ids):
                if wo.workflow_img:
                    workflow.append(wo.id)
                else:
                    err_name += wo.name+','
            if err_name:
                raise ValidationError('工作流【%s】的工作流图形未保存，请保存后下载！'%err_name)
            workflow_ids_str = ','.join([str(i) for i in workflow])
            return {
                'type': 'ir.actions.act_url',
                'url': '/attachment/batch/download/?res_ids=%s&zip_name=%s&download_model=%s' % (workflow_ids_str, "工作流图形.zip", 'workflow'),
                'target': 'self',
            }


class WorkflowBatchReminder(models.TransientModel):
    _name = "workflow.batch.reminder"

    def batch_reminder(self):
        instance_objs = self.env['workflow.instance'].browse(self.env.context.get('active_ids'))
        for rec in instance_objs:
            rec.approve_document.action_reminders_wizard()
